为了解释我的问题,我将首先粘贴一些示例代码,然后问相关的问题。
template< typename... CONDITIONS >
struct all_true;
template<>
struct all_true<>
{
const static bool value = true;
};
template< typename CONDITION, typename... CONDITIONS >
struct all_true< CONDITION, CONDITIONS... >
{
const static bool value = CONDITION::value && all_true<CONDITIONS...>::value;
};
template< class T >
class myobject
{
struct placeholder {};
template< typename... Ts >
struct CheckVaradicArgs
{
typedef
typename std::enable_if<
all_true< std::is_convertible<Ts, T>... >::value
, placeholder >::type type;
};
template< typename... Ts >
myobject( placeholder check, Ts... params )
{}
public:
myobject( const myobject& other )
{
std::cout << "Copy constructor" << std::endl;
}
template< typename... Ts >
myobject( Ts... params )
: myobject( typename CheckVaradicArgs<Ts...>::type(), params... )
{
std::cout << "Ts constructor with " << sizeof...(params) << std::endl;
}
};
在上面的代码中,我有一个模板化的类型myobject。我们希望它有一个构造函数,该构造函数可以接受任意数量的类型,这些类型可以转换为myobject被模板化的类型。
另外,我希望有通常的复制构造函数以及其他可以使用myobject的特殊类型的构造函数。
我遇到的问题是如何编写使用可变模板的构造函数。显然,它不应该接受我有其他构造函数的任何类型。然而,它应该接受通过特定测试的类型(在本例中是is_convertible测试)。
我解决这个问题的方法,它与GCC一起工作,是上面的解决方案-可变构造函数将参数传递给第二个私有构造函数,该构造函数也接受一个虚拟参数。dummy参数允许SFINAE在其他参数不满足某些条件时忽略构造函数。
所以我的问题是:
1)这是一个可接受的解决方案吗?或者我只是幸运,GCC将我委托的私有构造函数视为公共构造函数的重载解析的一部分?
2)有没有更干净的方法来做这件事?
我已经搜索并找到了类似问题的答案,但我发现的答案是在函数而不是构造函数上实现的。它们在返回类型上使用enable_if来强制编译器将该函数从重载解析表中排除。
重载解析不考虑可访问性;这个检查以后再做。
你所做的实际上不是SFINAE;它不影响重载解析,只是使模板构造函数的实例化在被重载解析选择后变得不正确。
我们可以通过添加一个非模板构造函数来看到这一点,它不是一个很好的匹配:
template< class T >
class myobject
{
/* ... */
public:
/* ... */
myobject( const char * ptr )
{
std::cout << "const char * constructor" << std::endl;
}
template< typename... Ts >
myobject( Ts... params )
: myobject( typename CheckVaradicArgs<Ts...>::type(), params... )
{
std::cout << "Ts constructor with " << sizeof...(params) << std::endl;
}
};
char * p = nullptr;
myobject<int> something(p);
如果是SFINAE,模板将从重载解析中取出,并选择const char *
构造函数。相反,当编译器试图实例化可变模板时,您会得到一个编译错误(g++做同样的事情)。
template< class T >
class myobject
{
template< typename... Ts >
using CheckVariadicArgs =
typename std::enable_if<
all_true< std::is_convertible<Ts, T>... >::value, int
>::type;
public:
myobject( const myobject& other )
{
std::cout << "Copy constructor" << std::endl;
}
template< typename... Ts, CheckVariadicArgs<Ts...>* = nullptr >
myobject( Ts... params )
{
std::cout << "Ts constructor with " << sizeof...(params) << std::endl;
}
};
演示。