且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何显式实例化一个模板类,它有一个具有友元函数(C ++)的嵌套类

更新时间:2022-12-24 19:31:58

问题不在于 friend



问题是这个函数本身:

  template< typename T> 
void swap(typename Foo< T> :: Bar& b1,typename Foo< T> :: Bar& b2){} // line 26

无法从嵌套类中取得模板参数 T ,请参阅:在模板中输出嵌套类甚至更好这个答案: http://***.com/a/4092248/1463922



为了说明为什么无法做到这一点,考虑这个函数:

  template< class T> 
void foo(typename A< T> :: Bar);

这个定义:

  template< class T> 
struct A {typedef int Bar; };

此电话:

  int a; 
foo(a);

在这个例子中 T 是什么?是 int ,因为 A< int> :: Bar int $ c $因为
A< float> :: Bar 问题是你调用 foo< int>(int)或 foo< float>(int)或...



或者让示例更接近问题:

 模板< class T> 
struct Foo {
struct Bar {};
};

似乎编译器应该没有解决问题的方法:

 模板< class T> 
void resolve(typename Foo< T> :: Bar *);

但编译器甚至在这里有问题,因为它不能确定一些其他类的专门化不会使用inner其他类的struct,例如:

  template< class T& 
struct Foo< T *> {
typedef Foo< T> :: Bar Bar;
};

因此:

  Foo< void> :: Bar b; 
resolve(& b);

编译器无法知道调用哪个版本:

 解析< void>(Foo< void> :: Bar *); 
//或
resolve< void *>(Foo< void> :: Bar *);
// ^






建议你 - 使用内联的朋友 - 但实现它与一些其他模板类。这工作 - 但我相信这是一个有点过度设计:

 模板< class S& 
类ImplementSwap;

template< typename T>
class Foo {
public:
struct Bar {
int a;
Bar(){}
〜Bar(){}
friend class ImplementSwap< Foo< T>
friend void swap(Foo< T> :: Bar& b1,Foo< T> :: Bar& b2)
{ImplementSwap< Foo T> :: doSwap(b1,b2) }
Bar(Bar& b){swap(* this,b); }

};
};

template< class T>
class ImplementSwap< Foo< T>> {
public:
static void doSwap(typename Foo< T> :: Bar&,typename Foo< T> :: Bar&
};

template< class T>
void ImplementSwap< Foo< T>> :: doSwap(typename Foo< T> :: Bar&,typename Foo< T> :: Bar&)
{
// this one不是inline ....
}

我做Bar公开做这个测试:

  Foo< int> :: Bar a = Foo< int> :: Bar // move constructor 

int main(){
swap(a,a); // explicit swap
}






>
[OLD]
我以前的答案是完全错误的,第一个评论指它。

  friend void swap<>(typename Foo< T> :: Bar& typename Foo< T& ); 
// ^^

[/ OLD]
/ p>

Probably been asked before, but all this is approaching the limit of my comprehension and cognizance of C++, so I'm a little slow in understanding what's being talked about and exactly what's going on. Let me just jump straight to the code. This works:

template <typename T>
class Foo
{
    struct Bar
    {
        Bar() {}
        ~Bar() noexcept {}
        Bar(Bar&& b) : Bar() { swap(*this, b); }

        friend void swap(Bar& b1, Bar& b2) { /* ... */ }
    };
};

template class Foo<int>; // explicit instantiation of Foo with int type

But how do I move the definition of swap outside of the Bar struct body? If I do this:

template <typename T>
class Foo {
    struct Bar {
        // ...
        Bar(Bar&& b) : Bar() { swap(*this, b); } // line 16
        // ...
        template <typename V>
          friend void swap(typename Foo<V>::Bar&, typename Foo<V>::Bar&);
    };
};

template <typename T>
  void swap(typename Foo<T>::Bar& b1, typename Foo<T>::Bar& b2) {} // line 26

template class Foo<int>; // line 31

g++ (4.7.1, flags: -Wall -std=c++11) reports:

main.cpp: In instantiation of ‘Foo<T>::Bar::Bar(Foo<T>::Bar&&) 
            [with T = int; Foo<T>::Bar = Foo<int>::Bar]’:
main.cpp:31:16:   required from here
main.cpp:16:28: error: no matching function for call to 
            ‘swap(Foo<int>::Bar&, Foo<int>::Bar&)’
main.cpp:16:28: note: candidate is:
main.cpp:26:6: note: template<class T> void swap(typename Foo<T>::Bar&, 
                                                 typename Foo<T>::Bar&)
main.cpp:26:6: note:   template argument deduction/substitution failed:
main.cpp:16:28: note:   couldn't deduce template parameter ‘T’

I guess the code for swap also needs to be created when explicitly instantiating Foo, which makes sense, but why can't the compiler figure out that swap(Foo<int>::Bar&...) needs to be created? Why does the template substitution fail? Or have I got everything wrong?

UPDATE 1

With:

template <typename T> class Foo;
template <typename T>
  void swap(typename Foo<T>::Bar& b1, typename Foo<T>::Bar& b2);

template <typename T>
class Foo {
    struct Bar {
      Bar(Bar&& b) : Bar() { swap(*this, b); }  // line 19
      friend void swap<>(Foo<T>::Bar& b1, Foo<T>::Bar& b2); // line 20
    };
};

template <typename T>
  void swap(typename Foo<T>::Bar& b1, typename Foo<T>::Bar& b2) {} // line 26

template class Foo<int>; // line 29

g++ (4.7.1, flags: -Wall -std=c++11) reports:

main.cpp: In instantiation of ‘struct Foo<int>::Bar’:
main.cpp:29:16:   required from here
main.cpp:20:17: error: template-id ‘swap<>’ for ‘void swap(Foo<int>::Bar&, Foo<int>::Bar&)’ does not match any template declaration
main.cpp: In instantiation of ‘Foo<T>::Bar::Bar(Foo<T>::Bar&&) [with T = int; Foo<T>::Bar = Foo<int>::Bar]’:
main.cpp:29:16:   required from here
main.cpp:19:24: error: no matching function for call to ‘Foo<int>::Bar::Bar()’
main.cpp:19:24: note: candidate is:
main.cpp:19:5: note: Foo<T>::Bar::Bar(Foo<T>::Bar&&) [with T = int; Foo<T>::Bar = Foo<int>::Bar]
main.cpp:19:5: note:   candidate expects 1 argument, 0 provided
main.cpp:19:28: error: no matching function for call to ‘swap(Foo<int>::Bar&, Foo<int>::Bar&)’
main.cpp:19:28: note: candidate is:
main.cpp:26:8: note: template<class T> void swap(typename Foo<T>::Bar&, typename Foo<T>::Bar&)
main.cpp:26:8: note:   template argument deduction/substitution failed:
main.cpp:19:28: note:   couldn't deduce template parameter ‘T’

UPDATE 2

OK, so this can't be done. Piotr has linked to Output a nested class inside a template, but I don't understand the answer. Why can't swap be defined outside its declaration? As far as I (mis)understand things, why can't the compiler create code for swap(Foo<int>::Bar&...) and link to it in the code for the explicit instantiation of Foo<int>? Have I totally misunderstood what's going on? What's the problem?

UPDATE 3

OK, this can't be done because if there are template specializations the compiler can't guarantee calls to swap defined outside of Foo are unambiguous since Foo<some_class>::Bar might be something completely different in a particular specialization. I hope I've got that right. But, why doesn't g++ warn me about this before I create an explicit instantiation of Foo?

template <typename T>
class Foo {
    struct Bar {
        // ...
        Bar(Bar&& b) : Bar() { swap(*this, b); }
        // ...
        template <typename V>
          friend void swap(typename Foo<V>::Bar&, typename Foo<V>::Bar&);
    };
};

template <typename T>
  void swap(typename Foo<T>::Bar& b1, typename Foo<T>::Bar& b2) {}

//template class Foo<int>; // let's comment this explicit instantiation out.

This code compiles fine (g++ 4.7.1, flags: -Wall -std=c++11). But, shouldn't it warn me that this code could possibly cause problems? When I add the explicit instantiation of Foo, the problem isn't with that line itself, but with the swap code implemented outside of Foo.

The problem is not with friend.

The problem is with this function itself:

template <typename T>
void swap(typename Foo<T>::Bar& b1, typename Foo<T>::Bar& b2) {} // line 26

Deducing template parameter T from nested class is not possible, see: Output a nested class inside a template or even better this answer: http://***.com/a/4092248/1463922

To give example why it cannot be done, consider this function:

template <class T>
void foo(typename A<T>::Bar);

And this A definition:

template <class T>
struct A { typedef int Bar; };

And this call:

int a;
foo(a);

What is T in this example? Is it int because A<int>::Bar is int, OR float because A<float>::Bar is int too OR whatever you want.... The question is what function you are calling foo<int>(int) or foo<float>(int) or ...

Or to give example more closer to question:

template <class T>
struct Foo {
   struct Bar {}; 
};

It seems that compiler should have no problems with resolving this:

template <class T>
void resolve(typename Foo<T>::Bar*);

But compiler even here has problems because it is not sure if specialization of some other class will not use inner struct of other class, like this:

template <class T>
struct Foo<T*> {
   typedef Foo<T>::Bar Bar; 
};

So for:

Foo<void>::Bar b;
resolve(&b);

Compiler has no chance to know which version to call:

resolve<void>(Foo<void>::Bar*);
// or 
resolve<void*>(Foo<void>::Bar*);
//          ^   


What can I advice you - use inline friend - but implement it with some other template class. This works - but I am sure this is a little over-engineered:

template <class S>
class ImplementSwap;

template <typename T>
class Foo {
    public:
    struct Bar {
        int a;
        Bar() {}
        ~Bar() {}
        friend class ImplementSwap<Foo<T>>;
        friend void swap(Foo<T>::Bar& b1, Foo<T>::Bar& b2)
        {  ImplementSwap<Foo<T>>::doSwap(b1, b2); }
        Bar(Bar&& b)  { swap(*this, b); }

    };
};

template <class T>
class ImplementSwap<Foo<T>> {
public:
   static void doSwap(typename Foo<T>::Bar&,typename Foo<T>::Bar&);
};

template <class T>
void ImplementSwap<Foo<T>>::doSwap(typename Foo<T>::Bar&,typename Foo<T>::Bar&) 
{
  // this one is not inline....
}

I made Bar public to do this test:

Foo<int>::Bar a = Foo<int>::Bar(); // move constructor

int main() {
  swap(a,a); // explicit swap
}


[OLD] My previous answer was completely wrong, and first comments refer to it.

friend void swap<>(typename Foo<T>::Bar&, typename Foo<T>::Bar&);
//              ^^

[/OLD]