在不创建参数对象的情况下解析constexpr函数
Getting constexpr functions resolved without creating parameter objects
短版本:
如果我有这样的功能:
constexpr bool has_some_property(Foo) { return true; }
有没有任何方法可以调用函数而不必实际实例化Foo
?假设Foo
不是默认可构造的?
冗长版本:
Anthony Williams最近写了一篇文章,详细介绍了为任何专门化特定模板的enum class
对象启用的一组免费函数。它遵循<ios>
、std::is_error_code
中的类似方案,其中专门为用户定义的类型或值提供模板,以允许enable_if
启用某些功能。在安东尼的案例中:
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
,但在这种情况下不起作用。
有人能解决这个难题吗?
只是不要使用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
需要常量表达式的问题。
让我来回答你关于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
,所有代码都将以相同的方式工作。
这似乎是一个简单的问题:可以制作一个不带参数的函数模板。这样,您就不需要创建任何值来调用函数:)
您可以使用函数模板,也可以使用类模板。以下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
}
- 条件constexpr函数
- constexpr 函数中的非文字(通过 std::is_constant_evaluated)
- constexpr 函数获取常量字符*
- 如何在 constexpr 函数中实现回退运行时
- 在非 constexpr 函数中作为左值传递的变量上使用 'constexpr' 函数
- NVCC 错误:string_view.h:constexpr 函数返回是非常量
- constexpr函数中的静态constexpr变量
- 在 constexpr 函数中断言
- G++ 编译器是否在未使用返回值的情况下将 constexpr 函数视为常规函数?
- constexpr 函数的常量引用参数:gcc/msvc vs clang/icc
- constexpr 函数在编译时获取值,即使我的变量不是 constexpr
- 如何正确地对 constexpr 函数进行单元测试
- 为什么 std::launder 是一个 constexpr 函数?
- 我可以使用 constexpr 函数声明一个静态数组吗?
- 用于连接向量的 Constexpr 函数
- 在非constexpr函数上添加的constexpr限定符不会触发任何警告
- 为什么 std::get<T> 其中 T 是调用 constexpr 函数失败的结果?
- 在constexpr函数中插入许多模板
- 在enable_if_t中调用 constexpr 函数
- constexpr 函数中的 for 循环无法使用 MSVC 19.23 进行编译