is_convertable可用于多个参数

is_convertible for multiple arguments

本文关键字:参数 用于 convertable is      更新时间:2023-10-16

假设由于任何原因我没有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之前是没有意义的,因为没有办法隐式调用多参数构造函数。但现在我们有了大括号包围的初始值设定项列表语法,多参数构造函数上的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());
                           ^

这是明智的。I、 然而,期望test的这个过载sfinae out,而使用另一个过载,从而不会产生错误。

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

gcc最近进行了修补,4.9版将接受该代码。clang也接受它,所以代码可能很好。抱歉,这并不能告诉你如何解决旧版本的gcc的问题。