gpt4 book ai didi

c++ - 如果直接使用,constexpr 函数参数被认为是 constexpr,但如果用于调用另一个 constexpr 函数则不是

转载 作者:可可西里 更新时间:2023-11-01 15:17:43 24 4
gpt4 key购买 nike

在试验 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;
}

编辑 这里是相关的编译输出。

clang

 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 指出

If the implicitly-defined constructor would satisfy the requirements of a constexpr constructor (7.1.5), the implicitly-defined constructor is constexpr.

此外,如果隐式复制构造函数不是 constexpr,那么带有 constexpr 的显式默认声明应该导致程序格式错误,带有需要诊断(§8.4.2 [dcl.fct.def.default]/p2):

An explicitly-defaulted function may be declared constexpr only if it would have been implicitly declared as constexpr.

但是如果第二个 noop 调用被注释掉,两个编译器( clangg++ )都会编译第二个版本的代码。

关于c++ - 如果直接使用,constexpr 函数参数被认为是 constexpr,但如果用于调用另一个 constexpr 函数则不是,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25465379/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com