假设我出于某种原因没有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
的这种重载能够被取代,而另一种则会被使用,因此不会产生任何错误。
所以问题是:为什么不发生这种情况,我该怎么办呢?