当我尝试使用constexpr函数和模板(以及非类型的模板参数)时,我偶然发现了一个现象,我不知道是哪条规则使它生效。
因此,我的问题本质上是“为什么会发生这种情况”,根据有关警察的规则。“这个”如下。
在其中一个参数函数中,如果直接使用一个参数,那么在编译时计算中使用该参数就没有问题。(示例第2行)
当使用相同的参数作为另一个constexpr函数的参数时,编译器会抱怨这个表达式(参数id)不是constexpr。(示例第3行)
简言之:
template <typename T> constexpr std::size size (T obj) { return obj.size(); }
template <typename T> constexpr auto sz1 (T obj) { return std::make_index_sequence< obj.size() > { }.size(); } // OK ...
template <typename T> constexpr auto sz2 (T obj) { return std::make_index_sequence< size(obj) > { }.size(); } // ERROR
// "obj" is [suddenly] not a constexpr这发生在g++-4.9.1和clang++-3.4.2中。
下面是一个用于快速和简单实验的小测试程序。
#include <utility>
#include <array>
#include <iostream>
// utils
template <size_t N> using require_constexpr = std::make_index_sequence<N>;
template <typename...> constexpr void noop (void) { }
// size() wrappers
template <typename T> constexpr std::size_t size (T obj) { return obj.size(); }
template <typename T> constexpr auto sz1 (T obj) { return size(require_constexpr< obj.size() > { }); }
template <typename T> constexpr auto sz2 (T obj) { return size(require_constexpr< size(obj) > { }); }
int main0 (int, char**)
{
constexpr auto const ar = std::array<int, 4u> { 4, 5, 6, 7 };
// Check constexpr-computability of size(), sz1() and the expansion of sz2()
noop<
require_constexpr<
size(require_constexpr< ar.size() > { }) + sz1(ar) +
size(require_constexpr< size(ar) > { })
>
>();
// But this is an error
// ERROR: "obj" is not a constexpr in sz2()
//noop< require_constexpr< sz2(ar) > >();
return 0;
}这里的编辑是相对的编译输出。
嘎吱声
src/main1.cpp:12:87: error: non-type template argument is not a constant expression
template <typename T> constexpr auto sz2 (T obj) { return size(require_constexpr< size(obj) > { }); }
^~~~~~~~~
src/main1.cpp:28:32: note: in instantiation of function template specialization 'sz2<std::array<int, 4> >' requested here
noop< require_constexpr< sz2(ar) > >();
^
src/main1.cpp:12:92: note: read of non-constexpr variable 'obj' is not allowed in a constant expression
template <typename T> constexpr auto sz2 (T obj) { return size(require_constexpr< size(obj) > { }); }
^
src/main1.cpp:12:92: note: in call to 'array(obj)'
src/main1.cpp:12:49: note: declared here
template <typename T> constexpr auto sz2 (T obj) { return size(require_constexpr< size(obj) > { }); }
^gcc
src/main1.cpp: In substitution of ‘template<long unsigned int N> using require_constexpr = std::make_index_sequence<N> [with long unsigned int N = size<std::array<int, 4ul> >(obj)]’:
src/main1.cpp:12:102: required from ‘constexpr auto sz2(T) [with T = std::array<int, 4ul>]’
src/main1.cpp:28:38: required from here
src/main1.cpp:12:102: error: ‘obj’ is not a constant expression
template <typename T> constexpr auto sz2 (T obj) { return size(require_constexpr< size(obj) > { }); }
^
src/main1.cpp:12:102: note: in template argument for type ‘long unsigned int’ 发布于 2014-08-23 19:58:42
这看起来像一个错误,两个编译器如何对待编译器生成的副本构造函数。
此代码使用clang和g++进行编译。
#include <utility>
// utils
template <std::size_t N> struct require_constexpr { constexpr std::size_t size() const { return N; } };
struct test {
constexpr std::size_t size() const { return 0; }
constexpr test() { }
constexpr test(const test &) { }
};
template <typename...> constexpr void noop (void) { }
// size() wrappers
template <typename T> constexpr std::size_t size (T obj) { return obj.size(); }
template <typename T> constexpr auto sz1 (T obj) { return size(require_constexpr< obj.size() > { }); }
template <typename T> constexpr auto sz2 (T obj) { return size(require_constexpr< size(obj) > { }); }
int main (int, char**)
{
constexpr auto const ar = test();
// Check constexpr-computability of size(), sz1() and the expansion of sz2()
noop<
require_constexpr<
size(require_constexpr< ar.size() > { }) + sz1(ar) +
size(require_constexpr< size(ar) > { })
>
>();
noop< require_constexpr< sz2(ar) > >();
return 0;
}但如果我们改变路线
constexpr test(const test &) { }至
constexpr test(const test &) = default;然后,它在两个构造函数中编译(g++,clang),尽管这两个构造函数所做的事情完全没有区别(test是一个完全空的类),§12.8 class. empty /p13声明:
如果隐式定义的构造函数满足
constexpr构造函数(7.1.5)的要求,则隐式定义的构造函数是constexpr。
此外,如果隐式复制构造函数不是constexpr,那么带有constexpr的显式默认声明应该会导致程序格式错误,需要诊断(§8.4.2 dcl.fct.def.default/p2):
只有当显式默认函数被隐式声明为
constexpr时,它才可以声明为constexpr。
但是如果第二个clang调用被注释掉,这两个编译器(g++,noop )都会编译第二个版本的代码。
发布于 2014-11-19 03:16:27
sz1和sz2的主要区别在于,sz1将obj的地址传递给size成员函数,后者不是常数表达式的有效结果,但作为中间结果操作数很好。sz2对obj执行lvalue->rvalue转换以传递到size函数,而且由于obj不是常量,所以表达式非常量。
T.C.关于隐式构造函数与显式构造函数的观点是有趣的。区别之处在于,隐式平凡的复制构造函数按位复制,这涉及复制填充的(非常量)字节,而用户提供的复制构造函数不复制任何东西。但标准规定,隐式构造函数是按成员级复制的,因此应该同样对待它们。
不清楚的是它们都应该被拒绝还是同时被接受;严格阅读5.19意味着两者都应该被拒绝,因为两者都涉及到使用复制构造函数对obj的lvalue->rvalue转换。我已经向C++委员会提出了这个问题。
https://stackoverflow.com/questions/25465379
复制相似问题