我正在使用 tl::expected 并希望定义一个推导指南来创建我在 .cpp 文件中使用的特定类型的 tl::unexpected: (非常简单的例子)
class MyError {
public:
enum ErrorType : int {
none,
type1,
type2
};
MyError (ErrorType type, const std::string& description)
{
}
};
template<typename T>
tl::unexpected (MyError::ErrorType, const T&)->tl::unexpected<MyError>;
这段代码可以用 GCC 完美编译(尝试过 11 到 14 版本) 但无法使用 Clang 14 到 18 进行编译。此外,Clang 会给出奇怪的错误消息(就像语法完全损坏一样):
<source>:2476:35: error: expected ')'
2476 | tl::unexpected (MyError::ErrorType, const T&)->tl::unexpected<MyError>;
| ^
<source>:2476:16: note: to match this '('
2476 | tl::unexpected (MyError::ErrorType, const T&)->tl::unexpected<MyError>;
| ^
<source>:2476:16: error: cannot use parentheses when declaring variable with deduced class template specialization type
2476 | tl::unexpected (MyError::ErrorType, const T&)->tl::unexpected<MyError>;
| ^
<source>:2476:26: error: declaration of variable 'ErrorType' with deduced type 'tl::unexpected' requires an initializer
2476 | tl::unexpected (MyError::ErrorType, const T&)->tl::unexpected<MyError>;
| ^
<source>:2476:46: error: expected ';' at end of declaration
2476 | tl::unexpected (MyError::ErrorType, const T&)->tl::unexpected<MyError>;
| ^
| ;
<source>:2476:46: error: cannot use arrow operator on a type
5 errors generated.
Compiler returned: 1
推导指南中将MyError::ErrorType替换为int后:
template<typename T>
tl::unexpected (int, const T&)->tl::unexpected<MyError>;
错误消息(几乎)完全不同:
<source>:2476:17: error: expected unqualified-id
2476 | tl::unexpected (int, const T&)->tl::unexpected<MyError>;
| ^
<source>:2476:17: error: expected ')'
<source>:2476:16: note: to match this '('
2476 | tl::unexpected (int, const T&)->tl::unexpected<MyError>;
| ^
2 errors generated.
Compiler returned: 1
但是,一个简单的模板示例在 Clang 中编译时没有错误:
template<typename T1, typename T2>
class C
{
private:
T1 a;
T2 b;
};
template<typename T>
C (int, const T&)->C<int, const T&>;
所以我不知道问题出在哪里?在我的推导指南中,在 tl::expected 实现中或在 clang 中(因为代码在由 GCC 编译时编译并运行得很好)
这里是上面示例的编译器资源管理器链接: https://godbolt.org/z/z3Ybrx1he
示例代码位于最后(第 2450 行及以下),位于 tl::预期标头内容之后。看起来 clang 和 gcc 都不知道 std::expected,因此我使用 tl::expected。
正如[temp.deduct.guide]/3中的标准所述:
演绎指南应位于相应类模板所属的范围内,并且对于成员类模板,具有相同的访问权限。
因此,将用户定义的推导指南放在命名空间范围中可以在 both clang 和 gcc 中工作。但请注意 [namespace.std]/4.4:
如果 C++ 程序声明以下内容,则其行为是未定义的:
- 任何标准库类模板的推导指南。
我认为我们应该以同样的方式对待其他人的类模板。定义一种包装器并使用它似乎要好得多:
template<typename T>
tl::unexpected<MyError> MyError::as_unexpected(ErrorType, const T&);