在构造函数中限制可变模板的可接受方法



为了解释我的问题,我将首先粘贴一些示例代码,然后问相关的问题。

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++做同样的事情)。

对于SFINAE,只需添加一个额外的模板参数:
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;
    }
};

演示。

最新更新