使用非类型模板参数时的类模板参数推导

问题描述 投票:0回答:1

允许 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++ c++17
1个回答
0
投票

不幸的是,从 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 能够实现这一点。

© www.soinside.com 2019 - 2024. All rights reserved.