多个参数的is_convertible

yur*_*hek 15 c++ sfinae type-traits implicit-conversion c++11

假设我std::is_convertible无论出于何种原因并且想要自己实现它.该标准说明了以下几点:

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

To f() {
    return declval<From>();
}
Run Code Online (Sandbox Code Playgroud)

好吧,没什么大不了的,我可以这样做(注意参数顺序与之相反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;
};
Run Code Online (Sandbox Code Playgroud)

这似乎按预期工作,据我所知,同样的事情.

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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

让我们扩展定义:

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

To f() {
    return {declval<From>()...};
}
Run Code Online (Sandbox Code Playgroud)

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

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;
};
Run Code Online (Sandbox Code Playgroud)

这正确地报告true存在匹配的隐式构造函数,false如果不是matcing构造函数.但如果有一个明确的匹配构造函数(至少在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;
}
Run Code Online (Sandbox Code Playgroud)

错误是关于尝试隐式调用显式构造函数,在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());
                                                                                                 ^
Run Code Online (Sandbox Code Playgroud)

这是明智的.但是,我希望这种过载test能够解决,而另一种则需要使用,因此不会产生错误.

所以问题是:为什么不发生这种情况,我能做些什么呢?

Mar*_*sse 4

gcc 最近已修补,4.9 版将接受该代码。clang 也接受它,所以代码可能没问题。抱歉,这并没有告诉您如何解决旧版本 gcc 的问题。