【问题标题】:C++ template class, template member friend function matching rulesC++模板类、模板成员友元函数匹配规则
【发布时间】:2018-07-11 23:34:03
【问题描述】:

我有一个带有模板化友元函数声明的模板化类,当用更直接但看似等效的表达式声明时,它的签名不匹配:

link to example on online compiler

#include <type_traits>

template <typename Sig> class Base;
template <typename R, typename ... Args> class Base<R(Args...)> { };
template <typename Sig, typename T> class Derived;
template <typename Sig> struct remove_membership;

template <typename T, typename R, typename ... Args>
class Derived<R(Args...), T> : public Base<R(Args...)> {
  static void bar() { }

  // XXX: why are these two not equivalent, and only the 1st version successful?
  template <typename T2>
  friend auto foo(T2 const &) -> Base<typename
    remove_membership<decltype(&std::remove_reference_t<T2>::operator())>::type> *;
  template <typename T2>
  friend auto foo(T2 const &) -> Base<R(Args...)> *;
};

template <typename F, typename R, typename ... Args>
struct remove_membership<R (F::*)(Args...) const> {
  using type = R(Args...);
};

template <typename T>
auto foo(T const &) -> Base<typename 
remove_membership<decltype(&std::remove_reference_t<T>::operator())>::type> * 
{
  using base_param_t = typename remove_membership<
    decltype(&std::remove_reference_t<T>::operator())>::type;
  Derived<base_param_t, T>::bar();
  return nullptr;
}

int main(int, char **) { foo([](){}); } // XXX blows up if verbose friend decl. removed.

Derived&lt;R(Args...), T&gt; 的成员定义内部(例如,在bar() 的主体中),类型匹配,增加了我的困惑:

static_assert(std::is_same<Base<R(Args...)>, Base<typename  
  remove_membership<decltype(&std::remove_reference_t<T>::operator())>::type>>::value,
  "signature mismatch");

是否有围绕模板类模板成员函数(和友元函数)声明和实例化的规则使这些前面的声明在某些或所有情况下不同?

【问题讨论】:

    标签: c++ typetraits friend-function template-function


    【解决方案1】:
    template <typename T2>
    void foo(T2 const &)
    
    template <typename T2>
    auto foo(T2 const &)
    -> std::enable_if_t<some_traits<T2>::value>;
    

    是 2 种不同的重载。即使两者都返回void(有效时)。
    第二次重载使用 SFINAE。

    (是的,模板函数的不同之处仅在于返回类型与常规函数相反)。

    您的版本不相同但相似(&amp;std::remove_reference_t&lt;T&gt;::operator() 应该是有效的)

    你可以使用更简单的模板友元函数:

    template <typename T, typename R, typename ... Args>
    class Derived<R(Args...), T> : public Base<R(Args...)> {
      static void bar() { }
    
      template <typename T2>
      friend auto foo(T2 const &) -> Base<R(Args...)>*;
    };
    
    template <typename T>
    auto foo(T const &) -> Base<void()>* // friend with Derived<void(), U>
    {
      using base_param_t = typename remove_membership<
        decltype(&std::remove_reference_t<T>::operator())>::type;
      Derived<base_param_t, T>::bar();
      return nullptr;
    }
    

    Demo

    但是你必须实现不同版本的模板foo

    【讨论】:

    • 不幸的是,我需要保留函数Sig 的通用参数,并让template &lt;typename T&gt; auto foo(T const &amp;) -&gt; ... 可用于任何签名,而不仅仅是根据需要进行专业化。不过,我很欣赏您关于 SFINAE 细微差别的观点。
    • 您可以使用别名缩短当前代码:template &lt;typename T&gt; using FooReturnType = Base&lt;typename remove_membership&lt;decltype(&amp;std::remove_reference_t&lt;T&gt;::operator())&gt;::type&gt; *;,然后是 template &lt;typename T&gt; auto foo(T const &amp;) -&gt; FooReturnType&lt;T&gt;
    • 我曾想避免在命名空间范围内留下另一个帮助模板,但这是我的后备计划。
    【解决方案2】:

    问题可以简化为:

    template<class T>
    struct identity {
        using type=T;
    };
    
    class X {
        int bar();
    public:
        template<class T>
        friend T foo();
    };
    
    template<class T>
    typename identity<T>::type foo() { return X{}.bar(); }
    
    int main() {
        foo<int>(); // error: bar is a private member of X
    }
    

    尽管我们知道identity&lt;T&gt;::type 总是T,但编译器并不知道这一点,因此假设是错误的。稍后在代码中的某处可能会出现identity&lt;T&gt; 的特化,它解析为T 以外的某种类型。

    因此,当编译器看到foo 的第二个声明时,它不会假定它与之前声明的foo 是同一个朋友。

    【讨论】:

    • 那么在实际实例化之前的某个阶段,是否在调用(尝试的)友元模板函数体中进行友元访问检查?使用template &lt;class T&gt; friend auto foo(); 加上template &lt;class T&gt; auto foo() { return X{}.bar(); } 对您的代码有效,但与我上面的类似转换似乎没有。
    猜你喜欢
    • 1970-01-01
    • 2019-04-12
    • 1970-01-01
    • 2010-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-19
    相关资源
    最近更新 更多