允许 C++20
#include<iostream>
using namespace std;
// simple case, CTAD works
template<class T>
struct A {
A(const T&, int i =0) { cout << "i=" << i << "\n"; }
};
// now I want replace `i` in `A` constructor with an compile time constant - enable e.g. usage of `if constexpr(i==5)`.
// I try to do this via second non-type template parameter.
template<class T, int X>
struct B {
B(const T&, int i =X) { cout << "X=" << X << "\n"; }
};
int main(void)
{
long whatever =0;
A a1{whatever}; // CTAD works
A a2{whatever, 5}; // CTAD works
B<long, 2> b1{whatever}; // explicit specified template arguments works
B b2{whatever, 2}; // cannot deduce template arguments for 'B'
return 0;
}
可以推断非类型模板参数,我的一本书(C++17完整指南)展示了一个例子:
template<typename T, int SZ>
class MyClass {
public:
MyClass(T(&)[SZ]) {}
};
MyClass mc("hello"); // deduces T as const char and SZ as 6
但我失败了…我们没有 constexpr 函数参数,对吗?
7
最佳答案
3
而且我们没有 constexpr 函数参数,对吧?
我们没有 constexpr 参数。
但是我们可能具有以下 NTTP 类型:
所以你可能会
template<class T, int X>
struct B {
B(const T& t) : B(t, std::integral_constant<int, X>{}) {}
B(const T& t, std::integral_constant<int, X>) { // be part of CTAD
std::cout << "X=" << X << "\n";
}
};
template <int N>
using ic = std::integral_constant<int, N>;
int main()
{
B<long, 2> b1{42L}; // explicit specified template arguments works
B b2{42L, std::integral_constant<int, 2>{}};
B b3{42L, ic<42>{}};
}
3
-
很酷,std::integral_constant 只是让编译器满意的辅助工具,它甚至没有在 Bs 构造函数中被触及(可能完全被优化掉了)。我得到了类似部分 CTAG 的东西。只要推导出 T,就可以忍受。
– -
1否则,还是老办法:
template <int N, typename T> B<T, N> make_B(const T&);
和auto b = make_B<2>(42L);
。
– -
哦,我喜欢这种老方法!直接,没有弯路。装在小宏中,准备就绪。
–
|
不幸的是,从 C++23 开始,您不能有constexpr
函数参数(有迹象表明这在未来可能会成为可能,请参阅)。为了使非类型参数推导有意义,您需要有一个可以从中推导的编译时值。但由于您不能有编译时(constexpr
)函数参数,您不能有可以从中推导的编译时值,因此您不能从非类型函数参数中进行非类型参数推导。
然而,正如 Fedor Pikus 所说:“在 C++ 中,如果你不能做某事,但你真的很想做,那么你还是可以做到的”。在这种情况下,你需要将编译时值包装到编译时类型中,因为编译时类型可用于参数推导。我认为最简单的方法是使用 lambda。例如:
#include <iostream>
#include <type_traits>
template <class T>
struct B
{
B(const T&, auto c)
requires(std::is_invocable_r_v<int, decltype(c)>) {
std::cout << "X=" << c() << "\n";
}
};
int main() {
long whatever = 0;
B b{ whatever, []{ return 2; } };
return 0;
}
如果你想独辟蹊径,你甚至可以使用宏:
#define CreateB(Arg1, Arg2) B{Arg1, []{ return Arg2; }}
int main() {
long whatever = 0;
auto b = CreateB(whatever, 2);
return 0;
}
此外,根据用例,您可能无论如何都需要constexpr int
在其他类型中包含该值。在这种情况下,您可以使用该类型来推断constexpr
值:
#include <iostream>
template <int X>
struct MyType
{
// do stuff with X here ...
};
template <template <int> class T, int X>
struct B
{
// deducing both T and X from function argument
B(const T<X>&) {
std::cout << "X=" << X << "\n";
}
};
int main() {
MyType<2> whatever{};
B b{whatever};
return 0;
}
但是是的,到目前为止,这已经是最接近的了。我知道这可能很烦人。希望未来的 C++ 版本能够实现这一点。另一方面,正如 @cigien 指出的那样,这也可能永远不会发生。
6
-
“有迹象表明这将在 C++26 中实现”嗯!有提案吗
– -
2
– -
constexpr-parameters 提案之前曾被提出过,但据我了解,在常量表达式中使用参数存在根本问题,因此不太可能成为标准。
–
-
我从委员会相关人员那里听说,他们甚至在讨论 C++26。如果是这样,那应该很快就会知道。但你可能是对的,这可能永远不会发生。我会修改答案的这一部分以采取更保守的立场。
– -
@HolyBlackCat aka也
std::constant_wrapper
正在针对 C++26 进行讨论,并且根据 GitHub 上的帖子,它似乎“仍在讨论中”
–
|
使用 Jarod42s 的“老方法”,我在这里结束
template<class T, int X>
struct B {
B(const T&) { cout << "X=" << X << "\n"; }
};
template <int N, class T> B<T, N> make_B(const T& t) { return B<T, N>(t); }
#define MAKE_B(obj, val) make_B<val>(obj)
...
B b2 =MAKE_B("Whatever", 42);
|
X
因为没有任何东西可以推断它。请注意,X
是的默认值X
,但它们不必相同。您无法X
通过查看来推断i
。–
SZ
值来自类型信息(即数组的固定大小),并且可以用作 constexpr。但是,i
构造函数中的参数B
是方法参数,不能用作 constexpr。–
–
std::integral_constant
并X
从常量的值中推断出来。–
–
|