允许 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 函数参数,是吗?
不幸的是,从 C++23 开始,你不能有
constexpr
函数参数(有充分的迹象表明这在 C++26 中将成为可能)。为了使非类型参数推导有意义,您需要有一个可以从中推导的编译时值。但是由于你不能有编译时(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
{
static constexpr int value = X;
};
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++26 能够实现这一点。