gpt4 book ai didi

c++ - 如何检测类型是否可列表初始化?

转载 作者:太空狗 更新时间:2023-10-29 22:56:38 26 4
gpt4 key购买 nike

背景:我正在编写类似 Either<A, B> 的包装器类型,我想要 return {some, args};从返回 Either<A, B> 的函数开始工作函数返回 A 的确切时间或 B .但是,我还想检测何时两者 AB可以用 {some, args} 初始化,并产生错误以保护用户免受歧义。

检测一个类型是否为T可以从一些参数初始化,我试着写一个这样的函数:

template<typename T, typename... Args>
auto testInit(Args&&... args) -> decltype(T{std::forward<Args>(args)...});

// imagine some other fallback overload here...

我认为表达式 testInit<T>(some, args)应该在 T{some, args} 时有效有效——在下面的代码中,初始化 auto x = MyType{1UL, 'a'};有效,并且此测试也通过了:

struct MyType {
MyType(size_t a, char b) {}
};
auto x = MyType{1UL, 'a'}; // ok
static_assert(std::is_same<MyType, decltype(testInit<MyType>(1UL, 'a'))>::value, ""); // ok

但是,当我们从 std::initializer_list<char> 添加构造函数时,它打破了:

struct MyType {
MyType(size_t a, char b) {}
MyType(std::initializer_list<char> x) {} // new!
};
auto x = MyType{1UL, 'a'}; // still ok

// FAILS:
static_assert(std::is_same<MyType, decltype(testInit<MyType>(1UL, 'a'))>::value, "");

note: candidate template ignored: substitution failure [with T = MyType, Args = <unsigned long, char>]: non-constant-expression cannot be narrowed from type 'unsigned long' to 'char' in initializer list

auto testInit(Args&&... args) -> decltype(T{std::forward<Args>(args)...});
^ ~~~

为什么 Clang 拒绝解析我的 (size_t, char)支持 initializer_list 的构造函数构造函数? 如何正确检测是否return {some, args};将在返回 T 的函数中工作,无论它是聚合类型、用户定义的构造函数还是 initializer_list构造函数?

最佳答案

有点复杂

而且我并不是真正的专家,所以我可以说一些不完全准确的话:请对我所说的持保留意见。

首先:当你写的时候

auto x = MyType{1UL, 'a'};  // ok

调用的构造函数是初始化列表之一,而不是接收 std::size_t 的构造函数和 char .

这是有效的,因为第一个值 1UL是一个 unsigned long int但是有一个值(注意:)可以缩小到 char .那就是:因为 1UL 有效。是适合 char 的值.

如果你尝试

auto y = MyType{1000UL, 'a'};  // ERROR!

你得到一个错误,因为 1000UL不能缩小到 char .即:1000UL不适合 char .

这也适用于 decltype() :

decltype( char{1UL} )    ch1; // compile
decltype( char{1000UL} ) ch2; // ERROR

但是考虑这个函数

auto test (std::size_t s)
-> decltype( char{s} );

此函数立即给出编译错误。

你可以想:“但是如果将 1UL 传递给 test()decltype() 可以将 std::size_t 的值缩小为 char

问题在于 C 和 C++ 是强类型语言;如果你允许 test() ,工作,返回一个类型,当收到一些值 std::size_t ,您可以创建(通过 SFINAE)一个函数,该函数返回某些值的类型和另一种类型的另一种类型。从强类型语言的角度来看,这是 Not Acceptable 。

所以

auto test (std::size_t s)
-> decltype( char{s} );

仅在 decltype( char{s} ) 时才可接受对于 s 的所有可能值都是可接受的.即:test() Not Acceptable ,因为 std::size_t可以容纳 1000UL不适合 char .

现在稍微改变一下:制作test()一个模板函数

template <typename T>
auto test (T s)
-> decltype( char{s} );

现在test()编译;因为有类型 T所有值都可以缩小到 char ( T = char ,例如)。所以test() , 模板化,本质上并没有错。

但是当你将它与 std::size_t 一起使用时

decltype( test(1UL) ) ch;  // ERROR

你得到一个错误,因为 test()不能接受 std::size_t .既不是可以缩小到 char 的值.

这正是您的代码的问题。

你的 testInit()

template <typename T, typename... Args>
auto testInit(Args&&... args)
-> decltype(T{std::forward<Args>(args)...});

是可以接受的,因为有类型 TArgs...这样T{std::forward<Args>(args)...}是可以接受的(例如:T = intArgs... = int)。

但是T = MyTypeArgs... = std::size_t, char是 Not Acceptable ,因为使用的构造函数是初始化列表为 char 的构造函数和非全部std::size_t值可以缩小到 char .

结论:编译时出错 decltype(testInit<MyType>(1UL, 'a')因为编译时出错 MyType{1000UL, 'a'} .

奖励答案:我建议对您的 testInit() 进行改进(恕我直言) .

使用 SFINAE 和逗号运算符的强大功能,您可以编写

template <typename T, typename... Args>
auto testInit (Args ... args)
-> decltype( T{ args... }, std::true_type{} );

template <typename...>
std::false_type testInit (...);

所以你可以写一些static_assert()简单如下

static_assert( true == decltype(testInit<MyType>('a', 'b'))::value, "!"); 
static_assert( false == decltype(testInit<MyType>(1UL, 'b'))::value, "!");

后记:如果你想要 MyType(size_t a, char b) {}调用构造函数,可以使用圆括号

auto y = MyType(1000UL, 'a');  // compile!

所以如果你写 testInit()带圆括号

template <typename T, typename... Args>
auto testInit (Args ... args)
-> decltype( T( args... ), std::true_type{} );

template <typename...>
std::false_type testInit (...);

您通过了以下两项 static_assert()

static_assert( true == decltype(testInit<MyType>('a', 'b'))::value, "!"); 
static_assert( true == decltype(testInit<MyType>(1UL, 'b'))::value, "!");

关于c++ - 如何检测类型是否可列表初始化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47343146/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com