我正在尝试编写一个类模板,该模板选择根据MAX_SIZE
模板参数选择存储成员变量 raw 或作为指针。 当我尝试使用足够大的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;
}
//...
};
该类旨在成为节省空间的"弱类型变量"(不一定是速度高效的,它不应该经常调用)。"ld_"成员实际上是工会的一部分(连同char
、int
、bool
、float
等)。
我尝试用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
//...
有没有办法实现这一目标?(同时保留选择类MAX_SIZE
的可能性)
问题是当你写的时候
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}
{ }
我尝试用 std::enable_if 制作二传手,但无济于事......
这是因为 SFINAE 要启用/禁用类的方法,仅适用于模板方法,该方法对方法本身的模板参数进行测试,而不是对类的模板参数进行测试。
所以,在上面的例子中,我写了
template <typename T = TypeLD,
typename std::enable_if<true == std::is_pointer<T>::value, bool>::type = true>
所以启用/禁用测试是关于构造函数的 typenameT
、template 参数,而不是关于TypeLD
、完整类的模板参数。
如果你写
template <typename std::enable_if<true == std::is_pointer<TypeLD>::value, bool>::type = true>
您收到错误。
一个更简单的解决方案是提供一个不同的基类,通过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
实现为非动态方法,反之亦然。
这个小工具:
#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); } )();
}
dispatch,当用编译时常量std::integral_constant<T, t>
调用时,返回其第n个参数。 如果你std::true_type
传递它,那就是std::integral_constant<bool, true>
。
std::is_pointer<T>
继承自true_type
或false_type
。
然后我们传递调度 2 个 lambda。 在编译时选择一个。 然后我们运行返回值。 然后,它返回双精度或指向双精度的指针。
由于返回哪个是在编译时确定的,因此可以正常工作。
在 c++14 和 c++17 中,这变得容易得多,但我使用我得到的东西。 调度在 c++11 中可以很好地解决这类问题。