代码之家  ›  专栏  ›  技术社区  ›  Jeff

C++模板类,模板成员朋友函数匹配规则

  •  1
  • Jeff  · 技术社区  · 6 年前

    我有一个模板化的类,它有一个模板化的friend函数声明,当在一个更直接但看起来相当的表达式中声明时,它的签名没有匹配:

    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<R(Args...), T> (例如,在 bar() ,类型匹配,使我更加困惑:

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

    模板类模板成员函数(和友元函数)的delaration和instantiations是否有规则使得前面的声明在某些或所有情况下都不同?

    2 回复  |  直到 6 年前
        1
  •  2
  •   Jarod42    6 年前
    template <typename T2>
    void foo(T2 const &)
    
    template <typename T2>
    auto foo(T2 const &)
    -> std::enable_if_t<some_traits<T2>::value>;
    

    是两个不同的重载。即使两个人都回来了 void (有效时)。
    第二次过载使用sfinae。

    (是的,模板函数只能通过返回类型不同于常规函数)。

    你的版本不一样但很相似( &std::remove_reference_t<T>::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 是的。

        2
  •  1
  •   David G    6 年前

    问题可以归结为:

    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<T>::type 总是 T ,编译器不知道这一点,因此假设这样做是错误的。可能有一个专门的 identity<T> 在代码后面的某个地方,解析为 T型 是的。

    因此,当编译器看到 foo 不会以为是同一个朋友 之前声明过。

    推荐文章