【问题标题】:Can a friend of A<T> be also a friend of A<A<T>>?A<T>的朋友也可以成为A<A<T>>的朋友吗?
【发布时间】:2016-02-01 13:04:54
【问题描述】:

考虑以下代码:

#include <vector>

template<typename T> class Container;
template<typename T> Container<Container<T>> make_double_container(const std::vector<std::vector<T>>&);

template<typename T>
class Container {
    std::vector<T> v;
    friend Container<Container<T>> make_double_container<T>(const std::vector<std::vector<T>>&);

public:
    Container() {}
    explicit Container(std::vector<T> v) : v(v) {}
};

template<typename T>
Container<Container<T>> make_double_container(const std::vector<std::vector<T>>& v) {
    Container<Container<T>> c;
    for(const auto& x : v) {
        c.v.push_back(Container<T>(x));
    }
    return c;
}

int main() {
    std::vector<std::vector<int>> v{{1,2,3},{4,5,6}};
    auto c = make_double_container(v);
    return 0;
}

编译器告诉我:

main.cpp: In instantiation of 'Container<Container<T> > make_double_container(const std::vector<std::vector<T> >&) [with T = int]':
main.cpp:27:37:   required from here
main.cpp:8:20: error: 'std::vector<Container<int>, std::allocator<Container<int> > > Container<Container<int> >::v' is private
     std::vector<T> v;
                    ^
main.cpp:20:9: error: within this context
         c.v.push_back(Container<T>(x));

我认为这是正确的,因为make_double_containerContainer&lt;T&gt; 的朋友,但不是Container&lt;Container&lt;T&gt;&gt; 的朋友。我怎样才能让make_double_container 在这种情况下工作?

【问题讨论】:

  • 这个问题在理论上很有趣。但是向量的向量在某种程度上是邪恶的,所以它大多违背了目的。

标签: c++ c++14 friend friend-function


【解决方案1】:

显然,您可以将make_double_container 的每一个专业化都变成朋友:

template <typename U>
friend Container<Container<U>> make_double_container(const std::vector<std::vector<U>>&);

如果你想在不偏专业化或类似的情况下将友谊保持在最低限度,请尝试

template <typename> struct extract {using type=void;};
template <typename U> struct extract<Container<U>> {using type=U;};
friend Container<Container<typename extract<T>::type>>
     make_double_container(const std::vector<std::vector<typename extract<T>::type>>&);

Demo.

【讨论】:

  • 我正是想避免f&lt;U&gt; 成为A&lt;T&gt; 的朋友(第一个code-sn-p)。我认为您的第二个代码 sn-p 提供了“精确”的友谊,同时避免了编写许多专门的 f 的麻烦。聪明的把戏,+1!
【解决方案2】:

make_double_container 定义为S 的模板函数似乎可以使其编译和工作。

template<typename T>
class Container {
    std::vector<T> v;

    template<class S>
    friend Container<Container<S>> make_double_container(const std::vector<std::vector<S>>&);

public:
    Container() {}
    explicit Container(std::vector<T> v) : v(v) {}
};

http://coliru.stacked-crooked.com/a/bdc23a0451a2125b

当编译器看到以下结构中的某些内容时:

template<class T>
class X{};

当您指定 T 是什么时,它会实例化该类并将类型名称为 T 的所有内容替换为指定的类型。

当你写作时

 Container<Container<T>> c;

T实际上是Container&lt;T&gt;,而make_double_container变成了

Container<Container<Container<T>>> make_double_container(const std::vector<std::vector<Container<T>>>&); 

然后(在 main 内部)进入

Container<Container<Container<int>>> make_double_container(const std::vector<std::vector<Container<int>>>&); 

通过将友谊更改为:

 template<class S>
    friend Container<Container<S>> make_double_container(const std::vector<std::vector<S>>&);

你强制编译器从 Container&lt;Container&lt;T&gt;&gt; 的模板中找出 S 是什么,然后它会找出正确的 S 类型,即 int

【讨论】:

  • 您的解决方案会起作用,但它会使例如make_double_container&lt;std::string&gt;Container&lt;Container&lt;int&gt;&gt; 的朋友。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-19
  • 2015-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-10
相关资源
最近更新 更多