【问题标题】:c++ : how to remove cv-qualifiers of a type to access class functions?c++:如何删除类型的 cv 限定符以访问类函数?
【发布时间】:2020-06-20 05:59:31
【问题描述】:

这里是一个例子:

#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;
}

上面的代码编译并运行良好。但如果最后 3 行(对run() 的调用)未注释,则会出现以下编译错误:

(1)

main.cpp:7:48: 错误:‘display’不是‘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++ copy (reference) constructor and move constructor of class cohabitation

【问题讨论】:

  • std::decay_t 工作正常,live demo
  • @rafix07 我很困惑,这确实为我解决了(2)(c++17):onlinegdb.com/Byx2vo9brU
  • @rafix07 实际上它适用于我的机器 gcc 7.4。我想知道为什么它在 gdb online 上不起作用

标签: c++ template-meta-programming constexpr move-semantics perfect-forwarding


【解决方案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 无法编译
  • @Vince - 不确定但是......当你声明a2时,参数是B&amp;&amp;,所以(推导指南)a2变成A&lt;B&gt;;所以TB;调用print() 解释T,你说print() 中的类型TB;但是t,在print(),是一个T &amp;&amp;,所以变成B &amp;&amp;;但是你传递给print() t_ 成员;所以你将B &amp; 传递给等待B &amp;&amp; 的函数。
猜你喜欢
  • 2017-12-27
  • 1970-01-01
  • 2011-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-19
  • 2011-01-11
相关资源
最近更新 更多