简短版:
如果我的功能如下:
constexpr bool has_some_property(Foo) { return true; }
有没有办法调用函数而不必实际实例化Foo
?假设Foo
不是默认可构造的吗?
漫长的版本:
Anthony Williams最近wrote an article详细说明了为专门用于特定模板的任何enum class
对象启用的一组免费函数。它遵循<ios>
,std::is_error_code
中的类似方案,其中一个方法专门为用户定义的类型或值提供模板,以允许enable_if
启用某些功能。在Anthony的案例中:
template<>
struct enable_bitmask_operators<my_bitmask>{
static constexpr bool enable=true;
};
然后定义运算符时:
template<typename E>
typename std::enable_if<enable_bitmask_operators<E>::enable,E>::type
operator|(E lhs,E rhs){
这种技术的问题在于模板特化必须与原始模板位于同一名称空间中,因此这不起作用:
namespace mystuff {
enum class Foo {
...
};
// Fail: wrong namespace
template<>
struct enable_bitmask_operators<Foo> : std::true_type {}
另一种方法是使用constexpr
函数,该函数可以在与类相同的命名空间中解析:
namespace mystuff {
enum class Foo {
...
};
constexpr bool enable_bitmask_operators(Foo) { return true; }
然后定义:
template<typename E>
typename std::enable_if<enable_bitmask_operators(E()),E>::type
operator|(E lhs,E rhs){
关于这一点的好处是,即使使用嵌套类,它也能很好地工作。它的问题是它需要一个默认的可构造类。这适用于我们的enum class
示例,但它不能作为专业化问题的一般解决方案。因此,如果我们想象尝试使用constexpr
函数而不是其他类的模板特化,我们可能会遇到其他失败:
struct Foo {
Foo() = delete;
};
constexpr bool has_some_property(Foo) { return true; }
...
// Fail for Foo...use of deleted function
template<typename E>
typename std::enable_if<has_some_property(E()),E>::type doStuff() {}
它有点令人沮丧,因为我实际上需要要创建的对象,我只是希望ADL能够识别要调用的constexpr
函数。我一直在想应该有一些方法我可以说我想要这个功能,而不必实际创建对象。我曾与std::declval
玩过,但在这种情况下并不起作用。
有人看到解决这个窘境的方法吗?
答案 0 :(得分:8)
请勿使用constexpr
:
std::true_type has_some_property(Foo&& );
Foo
有吗?
using has_it = decltype(has_some_property(std::declval<Foo>()));
static_assert(has_it::value, "It should!");
这是一个未评估的上下文,因此我们永远不必调用任何Foo
构造函数。并且可以回避constexpr
需要常量表达式的问题。
答案 1 :(得分:0)
让我回答一下围绕enable_if
技巧的第一个问题:
#include <type_traits>
namespace LibBitmasks {
template<typename E>
struct is_bitmask: std::false_type {
};
}
template<typename E>
typename std::enable_if<LibBitmasks::is_bitmask<E>::value, E>::type operator |(E lhs, E rhs) {
return static_cast<E>(static_cast<typename std::underlying_type<E>::type>(lhs) |
static_cast<typename std::underlying_type<E>::type>(rhs));
}
namespace MyLib {
enum class Q {
Q1 = 1,
Q2 = 2,
Q3 = 3
};
}
namespace LibBitmasks {
template<>
struct is_bitmask<MyLib::Q>: std::true_type {
};
}
int main() {
using MyLib::Q;
return (Q::Q1 | Q::Q2) == Q::Q3 ? 0 : 42;
}
如果您担心全局命名空间污染(更具体地说,关于与其他operator |
重载的潜在冲突),那么您可以将其隐藏在LibBitmasks::ops
命名空间中。然后,只要您想使用运算符,只需说using namespace LibBitmasks::ops
,所有代码都将以相同的方式工作。
答案 2 :(得分:0)
似乎是一个简单的问题:可以制作不带参数的函数模板。这样,您根本不需要创建任何值即可调用该函数:)
您可以使用功能模板或类模板。选择了以下C ++ 11,C ++ 14和C ++ 17的变体,以便在使用给定C ++标准中可用的功能时提供最短的类型表达式。
#include <type_traits>
struct Foo {
Foo() = delete;
Foo(bool) {}
};
// C++11
template <typename T> constexpr bool has_some_property1() { return false; }
template <> constexpr bool has_some_property1<Foo>() { return true; }
// C++11
template <typename T> struct has_some_property2 : std::false_type {};
template <> struct has_some_property2<Foo> : std::true_type {};
// C++17
template <typename T> constexpr bool has_some_property2_v = has_some_property2<T>::value;
template<typename E> // C++11
typename std::enable_if<has_some_property1<E>(), E>::type doStuff1() { return {true}; }
template <typename E> // C++14 (enable_if_t)
typename std::enable_if_t<has_some_property1<E>(), E> doStuff2a() { return {true}; }
template <typename E>
typename std::enable_if_t<has_some_property2<E>::value, E> doStuff2b() { return {true}; }
template <typename E> // C++17 (..._v)
typename std::enable_if_t<has_some_property2_v<E>, E> doStuff2c() { return {true}; }
int main()
{
doStuff1<Foo>(); // compiles
doStuff2a<Foo>(); // compiles
doStuff2b<Foo>(); // compiles
doStuff2c<Foo>(); // compiles
#if 0
doStuff1<bool>(); // fails to compile
doStuff2a<bool>(); // fails to compile
doStuff2b<bool>(); // fails to compile
doStuff2c<bool>(); // fails to compile
#endif
}