将 std::conditional 与不可转换类型一起使用(原始与指针)

Using std::conditional with non-convertible types (raw vs pointer)

我正在尝试编写一个 class 模板,它根据 MAX_SIZE 模板参数选择存储成员变量原始或作为指针。 当我尝试使用足够大的 MAX_SIZE 实例化它以选择原始时,编译器仍会尝试编译我将值分配为指针的行并触发不可能的转换错误 - 即使它可能会优化它在某个时候排队。

template <int MAX_SIZE = sizeof(void*)>
class WeakVar {
    typedef typename std::conditional<sizeof(long double) <= MAX_SIZE, long double, long double*>::type TypeLD;
    TypeLD ld_;

public:
    WeakVar() {
        if(std::is_pointer<TypeLD>::value)
            ld_ = new long double(0); // error: cannot convert 'long double*' to 'TypeLD'{aka 'long double'}
        else
            ld_ = 0;
    }
    //...
};

class 是一个 space 高效的 "weak typed variable" (不一定是速度高效的,它并不意味着经常被调用)。 'ld_' 成员实际上是联合的一部分(以及 charintboolfloat 等)。

我尝试用 std::enable_if 制作二传手,但无济于事...

//...
WeakVar() { setLD(0); }

typename std::enable_if<std::is_pointer<TypeLD>::value>::type setLD(long double value) {
    ld_ = new long double(value);
}
typename std::enable_if<!std::is_pointer<TypeLD>::value>::type setLD(long double value) {
    ld_ = value;
}
// error: the second method cannot be overloaded with the first
//...

有办法实现吗? (同时保留选择 class 的 MAX_SIZE 的可能性)

一个更简单的解决方案是提供一个不同的基础 class 区分 std::conditional:

class WeakVarA {};
class WeakVarB {};

template <int MAX_SIZE = sizeof(void*)>
class WeakVar 
    : public typename std::conditional<sizeof(long double) <= MAX_SIZE, WeakVarA, WeakVarB>::type 
{
    // ...
};

然后只需将 WeakVarA 实施为动态方法并将 WeakVarB 实施为非动态方法,反之亦然。

问题是当你写

WeakVar() {
    if(std::is_pointer<TypeLD>::value)
        ld_ = new long double(0); // error: cannot convert 'long double*' to 'TypeLD'{aka 'long double'}
    else
        ld_ = 0;
}

编译器必须编译 if()

的情况

所以当 TypeLD 不是指针时,编译器必须编译

 ld_ = new long double(0);

解决方案:如果(当)您可以使用 C++17 或更新版本,请使用 if constexpr

    if constexpr (std::is_pointer<TypeLD>::value)
        ld_ = new long double(0);
    else
        ld_ = 0;

当测试的值在编译时已知时,引入它是为了不编译错误的代码。

否则(在 C++11 和 C++14 中)您可以编写两个不同的函数,然后使用 SFINAE 启用正确的函数。

通过示例(注意:代码未经测试)

template <typename T = TypeLD,
          typename std::enable_if<true == std::is_pointer<T>::value, bool>::type = true>
WeakVar () : ld_{new long double(0)}
 { }

template <typename T = TypeLD,
          typename std::enable_if<false == std::is_pointer<T>::value, bool>::type = true>
WeakVar () : ld_{0}
 { }

I tried making setters with std::enable_if, but to no avail...

这是因为 SFINAE enable/disable class 的方法仅适用于模板方法,测试方法本身的模板参数,而不是 [=50] 的模板参数=].

所以,在上面的例子中,我写了

template <typename T = TypeLD,
          typename std::enable_if<true == std::is_pointer<T>::value, bool>::type = true>

所以 enabling/disabling 测试是关于类型名 T、构造函数的模板参数,而不是关于 TypeLD、完整 class 的模板参数。

如果你写

template <typename std::enable_if<true == std::is_pointer<TypeLD>::value, bool>::type = true>

你得到一个错误。

这个小工具:

#define RETURNS(...) \
  noexcept(noexcept(__VA_ARGS__)) \
  -> decltype(__VA_ARGS__) \
  { return __VA_ARGS__; }

template<class S, class F0, class...Fs>
auto dispatch( std::integral_constant<S, S(0)>, F0&& f0, Fs&&... )
RETURNS( dispatch( std::forward<F0>(f0) ) )

template<class S, S s, class F0, class...Fs>
auto dispatch( std::integral_constant<S, s>, F0&&, Fs&&...fs )
RETURNS( dispatch( std::integral_constant<S, S(s-1)>{}, std::forward<Fs>(fs)... ) )

template<std::size_t N, class...Fs>
auto dispatch( Fs&&...fs )
RETURNS( dispatch( std::integral_constant<std::size_t, N>{}, std::forward<Fs>(fs)... ) )

可以提供帮助。

它执行编译时切换。

WeakVar() {
    ld_ = dispatch(std::is_pointer<TypeLD>{}, []{ return 0.; }, []{ return new long double(0); } )();
}

调度,当使用编译时常量 std::integral_constant<T, t> 调用时,return 是它的第 n 个参数。如果传std::true_type,就是std::integral_constant<bool, true>.

std::is_pointer<T> 继承自 true_typefalse_type.

然后我们传递 dispatch 2 lambdas。在编译时选择一个。然后我们 运行 的 return 值。然后它 return 要么是一个双精度数,要么是一个指向双精度数的指针。

return编辑哪一个是在编译时确定的,这很好用。

and this gets much easier, but I work with what I got. And dispatch works really well to solve this kind of problem in .