C++:如何去除类型的CV限定符以访问类函数?

3

这是一个例子:

#include <iostream>

template<typename T, 
         typename ... Args>
void print(T&& t, Args&& ... args)
{
    // line where compilation fails when the A::run is called
    if constexpr (std::is_invocable_v<decltype(&T::display),T*,Args...>)
      {
          t.display(std::forward<Args>(args)...);
      }
    else 
    {
        std::cout << "not applicable !" << std::endl;    
    }
}

template<typename T>
class A 
{
    public:

    A(T&& t):t_(t){}

    template <typename... Args>
    void run(Args&& ... args)
    {
        print<T,Args...>(t_,std::forward<Args>(args)...);
    }

    T t_;
};

template <typename T> A(T&) -> A<T&>;
template <typename T> A(T&&) -> A<T>;

class B 
{
  public:
  B(int value):value_(value){} 
  void display(int a, int b)
  {
      std::cout << value_ << " " 
                << a << " " 
                << b << std::endl; 
  }
  int value_;
};

int main()
{
    int v1=10;
    int v2=20;

    B b1{1};
    A a1{b1};
    a1.t_.display(v1,v2);

    A a2{B(2)};
    a2.t_.display(v1,v2);

    //a1.run(v1,v2); // (1)
    //a2.run(v1,v2); // (2)
    //a1.run(v1);

    return 0;
}

上面的代码编译和运行都很好。但是如果取消注释最后三行(调用run()),就会出现以下编译错误:

(1)

main.cpp:7:48: error: ‘display’ is not a member of ‘B&’

if constexpr (std::is_invocable_v<decltype(&T::display),T*,Args...>)

(2)

main.cpp:27:25: error: no matching function for call to ‘print(B&, int&, int&)’

    print<T,Args...>(t_,std::forward<Args>(args)...);
注意:
template <typename T> A(T&) -> A<T&>;
template <typename T> A(T&&) -> A<T>;

在这里解释:

c++类的复制(引用)构造函数和移动构造函数的共存问题


1
std::decay_t 运行良好,在线演示 - rafix07
@rafix07 我有点困惑,这个链接(c++17)对我解决了(2)的问题:https://onlinegdb.com/Byx2vo9brU - Vince
@rafix07,实际上它在我的机器上可以运行,gcc 7.4。我想知道为什么它在gdb在线上不能运行。 - Vince
1个回答

1
问题(1)和(2)是不同的问题。
问题(1)源于以下代码中的事实:std::is_invocable_v
template<typename T, 
         typename ... Args>
void print(T&& t, Args&& ... args)
{
    if constexpr (std::is_invocable_v<decltype(&T::display),T*,Args...>)
    //  no T but std::decay_t<T> ...............^...........^

你需要使用“已衰变”类型,而不是T(可能是一个引用)

我的建议是

template <typename T, typename ... Args>
void print (T && t, Args && ... args)
 {
   using U = std::decay_t<T>;

   if constexpr ( std::is_invocable_v<decltype(&U::display), U*, Args...> )
      t.display(std::forward<Args>(args)...);
   else 
      std::cout << "not applicable !" << std::endl;    
}

问题(2)源于以下print()调用中模板参数的解释。
template <typename... Args>
void run(Args&& ... args)
{
    print<T,Args...>(t_,std::forward<Args>(args)...);
}

你阻碍了正确的模板推导。

建议:让模板推导和完美转发正常工作,并按以下方式调用函数

    print(t_,std::forward<Args>(args)...);

这个可以工作。对于(2),虽然我可以理解模板推导可能更明智,但我仍然困惑为什么print<T,Args...>不能编译。 - Vince
1
@Vince - 不确定,但是...当你声明a2时,参数是一个B&&,所以(推导指南)a2变成了A<B>;所以TB;在调用print()时,明确说明T,你说在print()中类型TB;但是在print()中,t是一个T &&,所以变成了B &&;但是你将t_成员传递给了print(),所以你将一个B &传递给了一个等待B &&的函数。 - max66

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接