检测 C++ 14 中的 constexpr size() 成员函数
Detecting a constexpr size() member function in C++ 14
我有一些通用代码,想知道它何时被传递了一组对象,这些对象的数量在编译时是已知的,因为它可以选择另一种算法策略。为此,我尝试编写一个has_constexpr_size(T)
constexpr 函数,如下所示,该函数尝试探测 T 的size()
成员函数以查看它是否可以作为 constexpr 执行。
请注意,这里与通常的"我可以检测到 constexpr 执行上下文吗?"问题有一个关键区别,因为一些 STL 容器(如array<T>
)总是提供 constexpr 可用的size()
函数,而其他 STL 容器(如initializer_list<T>
)获得 constexpr 可用的size()
函数,当且仅当初始值设定项列表本身是 constexpr 时(因为实现取决于内部指针对, 这些需要是 constexpr 才能size()
拥有全 constexpr 输入)。因此,has_constexpr_size(T)
函数采用正在测试的类型T
实例,以便它可以检测这两种情况。
这是一个测试用例:
#include <array>
#include <type_traits>
#include <utility>
#include <vector>
namespace type_traits
{
namespace detail
{
template <size_t N> struct Char
{
char foo[N];
};
template <class T> constexpr inline Char<2> constexpr_size(T &&v) { return (v.size(), true) ? Char<2>() : throw 0; }
template <class T> inline Char<1> constexpr_size(...) { return Char<1>(); }
}
//! Returns true if the instance of v has a constexpr size()
template <class T> constexpr inline bool has_constexpr_size(T &&v)
{
return noexcept(detail::constexpr_size<T>(std::forward<T>(v)));
}
// Non-constexpr array (always has a constexpr size())
auto ca=std::array<int, 2>();
// Constexpr initializer_list (has constexpr size()). Note fails to compile on VS2015 as its initializer_list isn't constexpr capable yet
constexpr std::initializer_list<int> cil{1, 2};
// Non-constexpr initializer_list (does not have constexpr size())
std::initializer_list<int> il{1, 2};
// Non-constexpr vector (never has constexpr size())
std::vector<int> vec{1, 2};
// Passes on GCC 4.9 and clang 3.8
static_assert(ca.size(), "non-constexpr array size constexpr");
// Passes on GCC 4.9 and clang 3.8
static_assert(cil.size(), "constexpr il size constexpr");
// Fails as you'd expect everywhere with non-constexpr il error
//static_assert(il.size(), "non-constexpr il size constexpr");
// Passes on GCC 4.9, fails on VS2015 and clang 3.8
static_assert(has_constexpr_size(ca), "ca"); // Should NOT fail on VS2015 and clang 3.8
// Fails on GCC 4.9 and clang 3.8. VS2015 doesn't apply.
static_assert(has_constexpr_size(cil), "cil"); // FAILS, and it should not!
// Passes, correct
static_assert(!has_constexpr_size(il), "il");
// Passes, correct
static_assert(!has_constexpr_size(vec), "vec");
constexpr bool test()
{
return has_constexpr_size(std::initializer_list<int>{1, 2});
}
constexpr bool testval=test();
// Fails on GCC 4.9 and clang 3.8. VS2015 doesn't apply.
static_assert(testval, "test()");
}
您会注意到array<T>::size()
和 constexpr 的直接静态断言initializer_list<T>::size()
如您所期望的那样在所有编译器上都能正常工作。我的has_constexpr_size(T)
函数适用于array<T>
,但仅在GCC 4.9上,我认为这是因为GCC特别宽容的constexpr实现。我的has_constexpr_size(T)
函数在所有编译器上的 constexprinitializer_list<T>
中失败。
所以我的问题是:
是否可以编写一个
has_constexpr_size(T)
函数来正确检测array<T>
和 constexprinitializer_list<T>
的 constexpr 可用size()
成员函数以及提供 constexpr 可用size()
函数的任何其他类型?如果 (1) 目前在 C++ 或当前编译器中是不可能的,是否可以编写一个
has_constexpr_size(T)
函数来正确检测 constexpr 可用的size()
成员函数,用于array<T>
和任何其他类型提供始终 constexpr 可用的size()
函数?请注意,此解决方案不是检查是否存在简单的某个size()
函数,而是检查它是否可用。所以has_constexpr_size(std::vector<int>())
是错误的。
编辑:不幸的是,如果您将std::string
替换为类型而不是int
,则以下答案是错误的,例如std::array<std::string, 2>
你会发现它失败了。出于某种对我来说毫无意义的原因,C++ 14 将以下内容视为:
static_assert(static_cast<std::array<int, 2> *>(0)->size(), "foo"); // passes
static_assert(static_cast<std::array<std::string, 2> *>(0)->size(), "foo"); // fails
我正式傻眼了。无论如何,我将在下面留下答案以引起历史兴趣。
对于我自己的问题 2,我有一个有效的解决方案,其中使用表达式 SFINAE,我可以检测一个非零的 constexpr 可用的size()
成员函数array<>
并正确排除vector<>
,但它得到 constexprinitializer_list<>
错误。确认在GCC 4.9和clang 3.8和VS2015上工作:
#include <array>
#include <type_traits>
#include <utility>
#include <vector>
namespace type_traits {
namespace detail {
template <size_t N> struct Char { char foo[N]; };
// Overload only available if a default constructed T has a constexpr-available non-zero size()
template <class T, size_t N = T{}.size() + 1>
constexpr inline Char<N> constexpr_size(const T &) {
return Char<N>();
}
template <class T> constexpr inline Char<1> constexpr_size(...) {
return Char<1>();
}
}
//! Returns true if the instance of v has a constexpr size()
template <class T> constexpr inline bool has_constexpr_size(const T &v) {
return sizeof(detail::constexpr_size<typename std::decay<T>::type>(
std::move(v))) > 1;
}
// Non-constexpr array (always has a constexpr size())
auto ca = std::array<int, 2>();
// Constexpr initializer_list (has constexpr size()). Note fails to compile on
// VS2015 as its initializer_list isn't constexpr constructible yet
#ifndef _MSC_VER
constexpr std::initializer_list<int> cil{1, 2};
#endif
// Non-constexpr initializer_list (does not have constexpr size())
std::initializer_list<int> il{1, 2};
// Non-constexpr vector (never has constexpr size())
std::vector<int> vec{1, 2};
// Correct on GCC 4.9 and clang 3.8 and VS2015
static_assert(ca.size(), "non-constexpr array size constexpr");
// Correct on GCC 4.9 and clang 3.8.
#ifndef _MSC_VER
static_assert(cil.size(), "constexpr il size constexpr");
#endif
// Fails as you'd expect everywhere with non-constexpr il error
// static_assert(il.size(), "non-constexpr il size constexpr");
// Correct on GCC 4.9 and clang 3.8 and VS2015
static_assert(has_constexpr_size(ca), "ca");
// Incorrect on GCC 4.9 and clang 3.8 and VS2015
#ifndef _MSC_VER
static_assert(!has_constexpr_size(cil), "cil"); // INCORRECT!
#endif
// Correct on GCC 4.9 and clang 3.8 and VS2015
static_assert(!has_constexpr_size(il), "il");
// Correct on GCC 4.9 and clang 3.8 and VS2015
static_assert(!has_constexpr_size(vec), "vec");
constexpr bool test_ca() {
return has_constexpr_size(std::array<int, 2>{1, 2});
}
constexpr bool testca = test_ca();
// Correct on GCC 4.9 and clang 3.8 and VS2015
static_assert(testca, "testca()");
constexpr bool test_cil() {
return has_constexpr_size(std::initializer_list<int>{1, 2});
}
constexpr bool testcil = test_cil();
// Incorrect on GCC 4.9 and clang 3.8 and VS2015
static_assert(!testcil, "testcil()"); // INCORRECT!
}
因此,我们现在需要的只是一些过滤掉非constexpr输入类型的方法,以便我们可以正确地为非constexpr输入返回false。我怀疑这与堆栈溢出:(其他地方的未解决的问题相同
任何进一步的进步或想法都是非常受欢迎的。
如果我正在解释这个建议:
标准::is_constant_evaluated()
正确地,该标准截至 C++14 的措辞甚至没有澄清在编译时必须知道什么,什么不需要。而且,无论如何,C++20 中添加的这个"神奇库函数"无法在语言本身中实现。
GCC 9 的实现是:
constexpr inline bool
is_constant_evaluated() noexcept
{ return __builtin_is_constant_evaluated(); }
这是另一个迹象(尽管不是某个迹象),表明这无法使用其他语言结构实现。
- 对RValue对象调用的LValue ref限定成员函数
- 为什么使用 "this" 指针调用派生成员函数?
- 将公共但非静态的成员函数与ALGLIB集成
- 使用指向成员的指针将成员函数作为参数传递
- 将重载的成员函数传递给函数模板
- 我不小心调用了一个没有自己类对象的成员函数.但这是怎么回事呢
- 如何在C++中使用非静态成员函数作为回调函数
- C++错误C2600:无法定义编译器生成的特殊成员函数(必须首先在类中声明)
- 关联容器的下界复杂性:成员函数与非成员函数
- 在 C++ 中用派生类型重写成员函数
- 链表的泛型函数remove()与成员函数remove)
- 如何将lambda作为模板类的成员函数参数
- constexpr构造函数需要常量成员函数时出现问题
- 将自由函数绑定为类成员函数
- 区分非成员函数和头文件中的成员函数
- 如何从子成员函数修改父公共成员变量
- 保留对其他类的成员函数的引用
- 在运算符重载定义中使用成员函数(const错误)
- 内联如何影响模块接口中的成员函数
- 将成员函数指针作为参数传递给模板方法