你可以使用
std::enable_if
来筛选掉具有函数成员
value()
的类型
T
,并将真正的实现保持分离基于
static_assert
的构造器。如果
T
具有
value()
方法,则选择第一个构造器,并按照通常的方式实现(除了需要
std::enable_if
才能被选中):
template <typename T, typename = std::enable_if_t<HasValueMethod<T>::value>>
MyClass(const T &t) : m_value(t.value())
{}
因此,我们需要第二个构造函数被SFINAEd排除在函数重载之外,因为第一个构造函数已经知道T::value
的存在:
template <typename T, typename = std::enable_if_t<!HasValueMethod<T>::value>>
MyClass(const T &, ...)
{
static_assert(HasValueMethod<T>::value, "T must have a value() method");
}
请注意可变参数
...
:它是必需的,以便区分构造函数的原型,使其不与第一个重叠(它们需要不同,否则会导致编译错误的模糊原型)。您不需要向其传递任何内容,只需将其设置为不同的原型即可。
同样要注意
std::enable_if
的谓词是相同的,但是被否定了。当
HasValueMethod<T>::value
为false时,第一个构造函数将被SFINAEd函数重载,但第二个构造函数不会,然后触发静态断言。
在静态断言的参数中仍然需要使用
HasValueMethod<T>::value
,因此它取决于执行
T
。否则,只需在那里放置
false
将始终触发,无论是否被选择出来。
以下是当
T
没有
.value()
时GCC打印的内容:
main.cpp: In instantiation of 'MyClass::MyClass(const T&, ...) [with T = A; <template-parameter-1-2> = void]':
main.cpp:35:18: required from here
main.cpp:21:9: error: static assertion failed: T must have a value() method
static_assert(HasValueMethod<T>::value, "T must have a value() method");
^~~~~~~~~~~~~
这是Clang的:
main.cpp:21:9: error: static_assert failed "T must have a value() method"
static_assert(HasValueMethod<T>::value, "T must have a value() method");
^
总之,这种方法存在一个问题(正如评论中@T.C.指出的那样):
MyClass
现在从未求值的上下文的角度来看可以转换为任何东西。也就是说,
static_assert(std::is_convertible_v</*anything*/, MyClass>)
在C++20中,当有概念的希望时,这个问题可以很容易地通过使用
requires
子句来解决。
template <typename T>
requires HasValueMethod<T>::value
MyClass(const T &t) : m_value(t.value())
{}
您可以直接在
requires
子句中表达
HasValueMethod<T>
,就像这样:
template <typename T>
requires requires (T a) { { a.value() } -> int; }
MyClass(const T &t) : m_value(t.value())
{}
或将 HasValueMethod<T>
转化为一个真正的概念:
template <typename T>
concept HasValueMethod = requires (T a) {
{ a.value() } -> int;
};
template <typename T>
requires HasValueMethod<T>
MyClass(const T &t) : m_value(t.value())
{}
这些解决方案可以让
std::is_convertible_v<T, MyClass>
正常工作。
m_value(util_get_value(t))
。相信 NRVO 会做正确的事情,完成了。 - StoryTeller - Unslander Monica