如果直接使用 constexpr-function,则认为 constexpr 参数,但如果用于调用另一个 conste
constexpr-function parameter is considered constexpr if used directly but not if used to call another constexpr-function
在试验constexpr函数和模板(以及非类型模板参数(时,我偶然发现了一个现象,我无法理解哪个规则使其生效。
所以我的问题本质上是"为什么会发生这种情况",根据关于 constexpr-s 的规则。"这个"如下。
在其中一个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) > { }); }
^
海湾合作委员会
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’
这看起来像是两个编译器如何处理编译器生成的复制构造函数的错误。
此代码使用 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.copy]/p13指出
如果隐式定义的构造函数满足要求
constexpr
构造函数 (7.1.5(,隐式定义的 构造函数是constexpr
。
此外,如果未constexpr
隐式复制构造函数,则带有constexpr
的显式默认声明应该会导致程序格式不正确,需要诊断(§8.4.2 [dcl.fct.def.default]/p2(:
只有在以下情况下,才能声明显式默认函数
constexpr
它会被隐式声明为constexpr
.
但是两个编译器(clang,g++(如果第二个noop
调用被注释掉,则编译第二个版本的代码。
sz1 和 sz2 之间的主要区别在于 sz1 将 obj 的地址传递给 size 成员函数,这不是常量表达式的有效结果,但作为中间结果操作数很好。 SZ2 在 OBJ 上执行左值>rvalue 转换以传递给 size 函数,由于 Obj 不是常量,这使得表达式非常量。
T.C.关于隐式构造函数与显式构造函数的观点很有趣。 区别的来源是隐式平凡复制构造函数执行按位复制,这涉及复制填充的(非常量(字节,而用户提供的复制构造函数不复制任何内容。 但是标准说隐式构造函数执行成员复制,因此应该将它们视为相同。
目前尚不清楚的是它们是否应该都被拒绝或两者都被接受;对 5.19 的严格阅读表明两者都应该被拒绝,因为两者都涉及使用复制构造函数对 obj 进行左值>rvalue 转换。 我已经向C++委员会提出了这个问题。
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 如果返回 -1,时间() 的参数是否被修改?
- 如果模板没有可变参数,则 Lambda 被推导出为 std::function
- 如果需要转换,我可以在读取参数的同时将其移动到另一个参数吗?
- 如果模板参数是另一个模板的实例化,则键入特征测试
- 如果可推导类型上有替换,可变参数模板类型推导会使编译器崩溃
- C++如果两个模板函数都与参数列表匹配,将调用哪个模板
- 如果参数与数据成员的参数同名,该怎么办?
- 如果条件,当我想第二个参数时
- 文档在哪里说明如果参数不是从末端删除参数,则无法从QT信号插槽连接中删除参数?
- 如果参数具有成员变量,则专用化函数
- 如果参数传递两次,会发生什么情况?一次按值,一次按引用?是否会修改
- 如果参数条件为 true,则阻止执行返回函数
- 如果参数之一是字符串,则C 返回类型等于字符串
- 类方法上的C++类型特征:如果参数为枚举,则部分专用化
- 如果参数采用引用,在函数参数中传递 *this 会导致内存泄漏
- 如果参数创建本地副本,则使用参数"const [variable_type] &"的目的是什么?
- 是否有一种方法,有一个模板做具体的事情基于如果参数是模板类型和值
- 如果参数已存储,它们是否会被复制
- 如果参数中未传递任何值,如何创建具有默认值的对象