多个参数的is_convertible

时间:2013-12-31 16:46:58

标签: c++ c++11 implicit-conversion sfinae typetraits

假设我出于某种原因没有std::is_convertible并希望自己实现它。标准说明了这些内容:

  

当且仅当以下代码中的返回表达式格式正确时,才应满足模板特化is_convertible<From, To>的谓词条件,包括对函数返回类型的任何隐式转换:

To f() {
    return declval<From>();
}

好吧,没什么大不了的,我可以这样做(注意与std::is_convertible中相反的参数顺序,这是故意的,与问题无关):

template <typename To_, typename From_>
class my_is_convertible {
    private:
        template <typename To>
        struct indirector {
            indirector(To);
        };

        template <typename To, typename From>
        struct tag {};

        template <typename To, typename From>
        static auto test(tag<To, From>)
            -> decltype(indirector<To>(std::declval<From>()), std::true_type());
        static auto test(...)
            -> std::false_type;

    public:
        static constexpr bool value = decltype(test(tag<To_, From_>()))::value;
};

这似乎按预期工作,据我所知也是如此。

现在我可以区分隐式和显式(或根本没有)构造函数:

struct A {};
struct B {};

struct Test {
    Test(A);
    explicit Test(B);
}; 

int main() {   
    std::cout << my_is_convertible<Test, A>::value; // true 
    std::cout << my_is_convertible<Test, B>::value; // false
    return 0;
}

到目前为止,这么好。现在,我想对多个参数构造函数做同样的事情。这在c++11之前没有意义,因为没有办法隐式调用multiargument构造函数。但是现在我们有了括号初始化列表语法,而且多参数构造函数上的explicit关键字有所不同。

让我们扩展定义:

  

当且仅当以下代码中的返回表达式格式正确时,才应满足模板特化my_is_convertible_many<To, From...>的谓词条件,包括对函数返回类型的任何隐式转换:

To f() {
    return {declval<From>()...};
}

为了实现它,我采取了明显的方式:

template <typename To_, typename... From_>
class my_is_convertible_many {
    private:
        template <typename To>
        struct indirector {
            indirector(To);
        };

        template <typename To, typename... From>
        struct tag {};

        template <typename To, typename... From>
        static auto test(tag<To, From...>)
            -> decltype(indirector<To>({std::declval<From>()...}), std::true_type());
        static auto test(...)
            -> std::false_type;

    public:
        static constexpr bool value = decltype(test(tag<To_, From_...>()))::value;
};

如果没有匹配的构造函数,则在存在匹配的隐式构造函数和true时正确报告false。但是如果有一个明确的匹配构造函数(至少在gcc 4.8.1上),它就无法编译:

struct A {};
struct B {};
struct C {};

struct Test {
    Test(A, A);
    //Test(B, B);
    explicit Test(C, C);
}; 

int main() {    
    std::cout << my_is_convertible_many<Test, A, A>::value; // true, correct
    std::cout << my_is_convertible_many<Test, B, B>::value; // false, correct
    std::cout << my_is_convertible_many<Test, C, C>::value; // error
    return 0;
}

错误是关于尝试隐式调用显式构造函数,在gcc上听起来像这样:

main.cpp: In substitution of 'template<class To, class ... From> static decltype (((my_is_convertible_many<To_, From_>::indirector<To>)({(declval<From>)()...}), std::true_type())) my_is_convertible_many<To_, From_>::test(my_is_convertible_many<To_, From_>::tag<To, From ...>) [with To = To; From = {From ...}; To_ = Test; From_ = {C, C}] [with To = Test; From = {C, C}]':
main.cpp:21:73:   required from 'constexpr const bool my_is_convertible_many<Test, C, C>::value'
main.cpp:37:54:   required from here
main.cpp:17:97: error: converting to 'Test' from initializer list would use explicit constructor 'Test::Test(C, C)'
         static auto test(tag<To, From...>) -> decltype(indirector<To>({std::declval<From>()...}), std::true_type());
                                                                                                 ^

哪个是明智的。但是,我希望test的这种重载能够被取代,而另一种则会被使用,因此不会产生任何错误。

所以问题是:为什么不发生这种情况,我该怎么办呢?

1 个答案:

答案 0 :(得分:4)

gcc最近是patched,版本4.9将接受该代码。 clang也接受它,所以代码可能很好。这并没有告诉你如何使用旧版本的gcc解决这个问题,抱歉。