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

Zor*_*war 10 c++ templates

可能以前曾被问过,但所有这些都接近了我对C++的理解和认识的极限,所以我在理解被讨论的内容以及到底发生了什么方面有点慢.让我直接跳到代码.这有效:

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
Run Code Online (Sandbox Code Playgroud)

但是如何移动结构体swap外部的定义Bar呢?如果我这样做:

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
Run Code Online (Sandbox Code Playgroud)

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

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’
Run Code Online (Sandbox Code Playgroud)

我想swap在显式实例化时也需要创建代码Foo,这是有道理的,但为什么编译器无法确定swap(Foo<int>::Bar&...)需要创建?为什么模板替换失败?或者我弄错了什么?

更新1

附:

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
Run Code Online (Sandbox Code Playgroud)

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

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’
Run Code Online (Sandbox Code Playgroud)

更新2

好的,所以这不可能.Piotr已经链接到输出模板中的嵌套类,但我不明白答案.为什么不能swap在声明之外定义?至于我(错误)理解的事情,为什么编译器不能在代码中创建代码swap(Foo<int>::Bar&...)并链接到它的显式实例化Foo<int>?我完全误解了发生了什么事吗?有什么问题?

更新3

好的,这是不可能的,因为如果有模板特化,编译器不能保证对swap外部定义的调用Foo是明确的,因为Foo<some_class>::Bar在特定的特化中可能是完全不同的东西.我希望我做对了.但是,为什么在我创建显式实例化之前g ++没有警告过我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.
Run Code Online (Sandbox Code Playgroud)

这段代码编译得很好(g ++ 4.7.1,flags:-Wall -std = c ++ 11).但是,它不应该警告我这个代码可能会导致问题吗?当我添加显式实例化时Foo,问题不在于该行本身,而是在swap外部实现的代码Foo.

Pio*_*ycz 3

问题不在于friend.

\n\n

问题出在这个函数本身:

\n\n
template <typename T>\nvoid swap(typename Foo<T>::Bar& b1, typename Foo<T>::Bar& b2) {} // line 26\n
Run Code Online (Sandbox Code Playgroud)\n\n

从嵌套类推导模板参数T是不可能的,请参阅:在模板中输出嵌套类,或者更好的是这个答案: https ://stackoverflow.com/a/4092248/1463922

\n\n

为了举例说明为什么不能完成此操作,请考虑以下函数:

\n\n
template <class T>\nvoid foo(typename A<T>::Bar);\n
Run Code Online (Sandbox Code Playgroud)\n\n

还有这个 A 定义:

\n\n
template <class T>\nstruct A { typedef int Bar; };\n
Run Code Online (Sandbox Code Playgroud)\n\n

还有这个电话:

\n\n
int a;\nfoo(a);\n
Run Code Online (Sandbox Code Playgroud)\n\n

T这个例子里有什么?是int因为A<int>::Baris int,还是float因为 \nA<float>::Barint或者你想要的任何东西......问题是你正在调用什么函数foo<int>(int)foo<float>(int)或......

\n\n

或者举一个更接近问题的例子:

\n\n
template <class T>\nstruct Foo {\n   struct Bar {}; \n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

看来编译器应该没有问题来解决这个问题:

\n\n
template <class T>\nvoid resolve(typename Foo<T>::Bar*);\n
Run Code Online (Sandbox Code Playgroud)\n\n

但即使在这里,编译器也存在问题,因为它不确定其​​他类的特化是否不会使用其他类的内部结构,如下所示:

\n\n
template <class T>\nstruct Foo<T*> {\n   typedef Foo<T>::Bar Bar; \n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

因此对于:

\n\n
Foo<void>::Bar b;\nresolve(&b);\n
Run Code Online (Sandbox Code Playgroud)\n\n

编译器没有机会知道要调用哪个版本:

\n\n
resolve<void>(Foo<void>::Bar*);\n// or \nresolve<void*>(Foo<void>::Bar*);\n//          ^   \n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

我可以建议你什么 - 使用内联朋友 - 但用其他一些模板类来实现它。这是可行的 - 但我确信这有点过度设计:

\n\n
template <class S>\nclass ImplementSwap;\n\ntemplate <typename T>\nclass Foo {\n    public:\n    struct Bar {\n        int a;\n        Bar() {}\n        ~Bar() {}\n        friend class ImplementSwap<Foo<T>>;\n        friend void swap(Foo<T>::Bar& b1, Foo<T>::Bar& b2)\n        {  ImplementSwap<Foo<T>>::doSwap(b1, b2); }\n        Bar(Bar&& b)  { swap(*this, b); }\n\n    };\n};\n\ntemplate <class T>\nclass ImplementSwap<Foo<T>> {\npublic:\n   static void doSwap(typename Foo<T>::Bar&,typename Foo<T>::Bar&);\n};\n\ntemplate <class T>\nvoid ImplementSwap<Foo<T>>::doSwap(typename Foo<T>::Bar&,typename Foo<T>::Bar&) \n{\n  // this one is not inline....\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我将 Bar 公开来进行此测试:

\n\n
Foo<int>::Bar a = Foo<int>::Bar(); // move constructor\n\nint main() {\n  swap(a,a); // explicit swap\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

\n[OLD]\n我之前的回答完全错误,第一个评论引用了它。

\n\n
friend void swap<>(typename Foo<T>::Bar&, typename Foo<T>::Bar&);\n// \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0^^\n
Run Code Online (Sandbox Code Playgroud)\n\n

[/旧]\n

\n