C++函数模板从成员类型中推导类型?

C++ funtion template deduction of type from member type?

我正在尝试编写一个 C++ 函数模板,当使用 classes 成员别名的参数调用时,该函数可以推导出该别名的 class。 简化的 版本会更好:

class A {
  public :
    using x_t = float;
    void whatami() {printf("A\n");}
};

class B {
  public :
    using x_t = int;
    void whatami() {printf("B\n");}
};

template<typename T>
void fn(T::x_t x) {
  T t;
  t.whatami();
};

...
A::x_t a;
fn(a);
...

g++ 7.3 错误是:

play$ g++ --std=c++11 x.cpp
x.cpp:19:12: error: variable or field ‘fn’ declared void
 void fn(T::x_t x) {
            ^~~
x.cpp:19:16: error: expected ‘)’ before ‘x’
 void fn(T::x_t x) {
                ^
x.cpp: In function ‘int main(int, char**)’:
x.cpp:29:3: error: ‘fn’ was not declared in this scope
   fn(a);
   ^~

我将其解释为 C++ 无法像 fn(T::x_t x) 那样推导出 T 但我不确定。

想法?

下面的函数应该使用对象(T),因为你想调用它的成员函数。

template<typename T>
void fn(T& x) 
{
  x.whatami();
};

模板函数的调用方式如下:

A a;
fn(a);

它将打印结果为 => A 这是你想要的,对吧?

它不能从float推导出T,这就是a。在尝试填充您的模板时,它不会对 typedef 进行任何特殊考虑。它也不会在整个类型 space 中搜索具有该名称成员的任何类型。

获得该推导的一种方法是使用对所属 class 的引用来提升该成员类型,并添加一个隐式转换回原语:

template <typename TOwner, typename TData>
class x_t {
    TData data;
public:
    constexpr x_t() = default;
    constexpr x_t(TData data) : data(data) { }
    constexpr operator TData() const { return data; }
};

struct A {
    using x_t = x_t<A, float>;
    /*...*/
};

然后部分特化将正常工作:

template<typename TOwner, typename TData>
void fn(x_t<TOwner, TData> x) {
  TOwner t;
  t.whatami();
};

并且您可以在任何类型安全的函数中使用实例

A::x_t a(42.0);
std::cout << a; // 42.0

(遗憾的是,printf 不算作类型安全。在这里使用流,优秀的 fmt 库中的 fmt::printf 也可以)。

它可能无法达到您容易从成员推断的目标,但这是可能的。

从任意模板id推导就像解任意方程,这远远超出了编译器的能力。想想这个:

template <int N>
using S = std::integral_constant<int, /* a super complex radical involving 42nd powers and 84th roots */>;

template <int N>
constexpr int solve(S<N>)
{
    return N;
}

你觉得编译器可以solve(std::integral_constant<int, 420>{});?没有。

在您的情况下,模板 ID 仅限于有限数量的情况,因此帮助编译器的一种简单的非侵入式方法是您自己求解方程式:

template <typename x_t>
using solve = std::conditional_t<
    std::is_same_v<x_t, float>, A,
    std::conditional_t<
        // similar code for B
    >
>;