【问题标题】:C++ funtion template deduction of type from member type?C++ 函数模板从成员类型中推导类型?
【发布时间】:2019-12-08 04:22:50
【问题描述】:

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

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,但我不确定。

想法?

【问题讨论】:

  • 这样的扣除是不可能的。如果允许,那将是非常低效的。编译器需要检查x_t 成员的所有可能类型,并将其与函数参数的类型进行比较,如果两个类匹配会发生什么?您可能想要添加为什么您需要此功能。可能有一种更简单的方法。
  • 谢谢。我从您的评论(和 parktomatomi)中得知,当编译器看到 fn(a) 时,它实际上并不知道 a 是 A::x_t。在那种情况下,我明白为什么这会成为一个问题。至于为什么?这是对某事的早期尝试,当我做不到时变成了智力挑战。最后,我正在做一些完全不同的事情。感谢所有回复的人。
  • 是的,在名称查找之后,A::x_tfloat 完全相同。 using 没有声明新类型。它只是为该类型提供一个别名。如果你写 float a; 而不是 A::x_t a; 它是完全相同的,并且该语言不提供任何方法来确定 a 是否已使用 floatA::x_t 作为类型声明。

标签: c++ template-argument-deduction function-templates


【解决方案1】:

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

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

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 也可以工作)。

这可能达不到您轻松从成员那里推断的目标,但这是有可能的。

【讨论】:

    【解决方案2】:

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

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

    模板函数的调用如下:

    A a;
    fn(a);
    

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

    【讨论】:

    • 我认为您正在规避 OP 想要解决的实际问题。他们似乎想推断出包含正确类型别名的类。他们最初不知道他们想要调用的类类型。
    【解决方案3】:

    从任意模板 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&lt;int, 420&gt;{});吗?没有。

    在您的情况下,模板 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
        >
    >;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-27
      • 1970-01-01
      • 2021-06-22
      相关资源
      最近更新 更多