内部 class 使用外部 class 模板参数但仅在使用 `ostream` 时编译失败
Compilation failure when inner class uses outer class template parameter but only when using `ostream`
当内部 class 使用外部 class 的模板参数时,我 运行 进入编译器错误,并且我在外部 class 上实例化输出流运算符] 在内部类型的成员上。
我花了很多时间试图解决这个问题。我相信以下来源很接近,但我仍然不明白为什么我遇到编译失败。
- https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-types
- https://msdn.microsoft.com/en-us/library/71dw8xzh.aspx
代码如下:
#include <iostream>
#include <vector>
template <typename T>
struct Outer
{
struct Inner
{
Inner(const T& val = T());
T data_;
}; // end class Inner
Outer();
void AddInnerChildToOuter(const T& data);
std::vector<typename Outer<T>::Inner> innerChildren_;
}; // end class Outer
// Inner constructor
template <typename T>
Outer<T>::Inner::Inner(const T& val) : data_(val)
{
}
template <typename T>
std::ostream& operator<<(std::ostream& strm, // Line 27
const typename Outer<T>::Inner& gn)
{
strm << gn.data_ << std::endl;
return strm;
}
// Outer constructor
template <typename T>
Outer<T>::Outer()
{
}
template <typename T>
void Outer<T>::AddInnerChildToOuter(const T& data)
{
typename Outer<T>::Inner node(data);
innerChildren_.push_back(node);
}
template <typename T>
std::ostream& operator<<(std::ostream& strm, const Outer<T>& g)
{
for (size_t i = 0; i < g.innerChildren_.size(); ++i)
std::cout << g.innerChildren_[i] << std::endl; // Line 51
return strm;
}
int main()
{
Outer<int> g;
g.AddInnerChildToOuter(3);
g.AddInnerChildToOuter(5);
std::cout << g << std::endl; // Line 60
return 0;
}
我在外部的 ostream operator <<
上收到一个编译器错误,它为内部调用了相应的输出流运算符。我不会发布编译器错误消息的全部内容;正是我认为相关的内容。
$ g++ -Wall -W -Wextra -pedantic -ansi OuterInnerArgh.cpp
OuterInnerArgh.cpp: In instantiation of ‘std::ostream& operator<<(std::ostream&, const Outer<T>&) [with T = int; std::ostream = std::basic_ostream<char>]’:
OuterInnerArgh.cpp:60:18: required from here
OuterInnerArgh.cpp:51:19: error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘const Outer<int>::Inner’)
std::cout << g.innerChildren_[i] << std::endl;
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
(截取了编译器对各种 ostream
重载的尝试;下面有更多编译器错误消息)
OuterInnerArgh.cpp:27:15: note: candidate: template<class T> std::ostream& operator<<(std::ostream&, const typename Outer<T>::Inner&)
std::ostream& operator<<(std::ostream& strm,
^~~~~~~~
OuterInnerArgh.cpp:27:15: note: template argument deduction/substitution failed:
OuterInnerArgh.cpp:51:19: note: couldn't deduce template parameter ‘T’
std::cout << g.innerChildren_[i] << std::endl;
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
OuterInnerArgh.cpp:48:15: note: candidate: template<class T> std::ostream& operator<<(std::ostream&, const Outer<T>&)
std::ostream& operator<<(std::ostream& strm, const Outer<T>& g)
^~~~~~~~
OuterInnerArgh.cpp:48:15: note: template argument deduction/substitution failed:
OuterInnerArgh.cpp:51:19: note: ‘const Outer<int>::Inner’ is not derived from ‘const Outer<T>’
std::cout << g.innerChildren_[i] << std::endl;
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
(截取了其余的编译器错误)
请告诉我为什么遇到编译器错误 -
- 尽管我有
ostream operator <<
typename Outer<T>::Inner&
- 即使我在相关的地方“洒”了
typename
“魔法尘埃”
- 仅适用于外部
ostream
运算符,不适用于构造函数或内部 ostream
运算符(后者可能根本没有实例化?)
为什么编译器说 ‘const Outer<int>::Inner’ is not derived from ‘const Outer<T>’
? (是的,没有继承,但内部类型定义嵌套在外部)
您遇到编译错误,因为 non-deduced context 导致模板参数推导失败。
In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction, but instead use the template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.
- The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:
例如,如果您明确指定模板参数(以丑陋的风格),它将编译。在 operator<<
中 Outer
:
operator<< <T> (strm, g.innerChildren_[i]);
// ^^^
你可以让 operator<<
成为非模板(绕过类型推导的麻烦),然后你必须在 class 定义中将它定义为 friend
。例如
struct Inner
{
Inner(const T& val = T());
T data_;
friend std::ostream& operator<<(std::ostream& strm,
const Inner& gn)
{
strm << gn.data_ << std::endl;
return strm;
}
};
解决这个问题
std::cout << g.innerChildren_[i] << std::endl; // Line 51
成为
std::cout << g.innerChildren_[i].data_ << std::endl; // Line 51
因为您试图在未定义的地方使用运算符 <<
如果依赖第(27)行中定义的运算符,修改第(50)行以正确调用它,如下
for (size_t i = 0; i < g.innerChildren_.size(); ++i)
{
operator<< <T>(strm, g.innerChildren_[i]);
strm << std::endl;
}
当内部 class 使用外部 class 的模板参数时,我 运行 进入编译器错误,并且我在外部 class 上实例化输出流运算符] 在内部类型的成员上。
我花了很多时间试图解决这个问题。我相信以下来源很接近,但我仍然不明白为什么我遇到编译失败。
- https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-types
- https://msdn.microsoft.com/en-us/library/71dw8xzh.aspx
代码如下:
#include <iostream>
#include <vector>
template <typename T>
struct Outer
{
struct Inner
{
Inner(const T& val = T());
T data_;
}; // end class Inner
Outer();
void AddInnerChildToOuter(const T& data);
std::vector<typename Outer<T>::Inner> innerChildren_;
}; // end class Outer
// Inner constructor
template <typename T>
Outer<T>::Inner::Inner(const T& val) : data_(val)
{
}
template <typename T>
std::ostream& operator<<(std::ostream& strm, // Line 27
const typename Outer<T>::Inner& gn)
{
strm << gn.data_ << std::endl;
return strm;
}
// Outer constructor
template <typename T>
Outer<T>::Outer()
{
}
template <typename T>
void Outer<T>::AddInnerChildToOuter(const T& data)
{
typename Outer<T>::Inner node(data);
innerChildren_.push_back(node);
}
template <typename T>
std::ostream& operator<<(std::ostream& strm, const Outer<T>& g)
{
for (size_t i = 0; i < g.innerChildren_.size(); ++i)
std::cout << g.innerChildren_[i] << std::endl; // Line 51
return strm;
}
int main()
{
Outer<int> g;
g.AddInnerChildToOuter(3);
g.AddInnerChildToOuter(5);
std::cout << g << std::endl; // Line 60
return 0;
}
我在外部的 ostream operator <<
上收到一个编译器错误,它为内部调用了相应的输出流运算符。我不会发布编译器错误消息的全部内容;正是我认为相关的内容。
$ g++ -Wall -W -Wextra -pedantic -ansi OuterInnerArgh.cpp
OuterInnerArgh.cpp: In instantiation of ‘std::ostream& operator<<(std::ostream&, const Outer<T>&) [with T = int; std::ostream = std::basic_ostream<char>]’:
OuterInnerArgh.cpp:60:18: required from here
OuterInnerArgh.cpp:51:19: error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘const Outer<int>::Inner’)
std::cout << g.innerChildren_[i] << std::endl;
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
(截取了编译器对各种 ostream
重载的尝试;下面有更多编译器错误消息)
OuterInnerArgh.cpp:27:15: note: candidate: template<class T> std::ostream& operator<<(std::ostream&, const typename Outer<T>::Inner&)
std::ostream& operator<<(std::ostream& strm,
^~~~~~~~
OuterInnerArgh.cpp:27:15: note: template argument deduction/substitution failed:
OuterInnerArgh.cpp:51:19: note: couldn't deduce template parameter ‘T’
std::cout << g.innerChildren_[i] << std::endl;
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
OuterInnerArgh.cpp:48:15: note: candidate: template<class T> std::ostream& operator<<(std::ostream&, const Outer<T>&)
std::ostream& operator<<(std::ostream& strm, const Outer<T>& g)
^~~~~~~~
OuterInnerArgh.cpp:48:15: note: template argument deduction/substitution failed:
OuterInnerArgh.cpp:51:19: note: ‘const Outer<int>::Inner’ is not derived from ‘const Outer<T>’
std::cout << g.innerChildren_[i] << std::endl;
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
(截取了其余的编译器错误)
请告诉我为什么遇到编译器错误 -
- 尽管我有
ostream operator <<
typename Outer<T>::Inner&
- 即使我在相关的地方“洒”了
typename
“魔法尘埃” - 仅适用于外部
ostream
运算符,不适用于构造函数或内部ostream
运算符(后者可能根本没有实例化?)
为什么编译器说 ‘const Outer<int>::Inner’ is not derived from ‘const Outer<T>’
? (是的,没有继承,但内部类型定义嵌套在外部)
您遇到编译错误,因为 non-deduced context 导致模板参数推导失败。
In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction, but instead use the template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.
- The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:
例如,如果您明确指定模板参数(以丑陋的风格),它将编译。在 operator<<
中 Outer
:
operator<< <T> (strm, g.innerChildren_[i]);
// ^^^
你可以让 operator<<
成为非模板(绕过类型推导的麻烦),然后你必须在 class 定义中将它定义为 friend
。例如
struct Inner
{
Inner(const T& val = T());
T data_;
friend std::ostream& operator<<(std::ostream& strm,
const Inner& gn)
{
strm << gn.data_ << std::endl;
return strm;
}
};
解决这个问题
std::cout << g.innerChildren_[i] << std::endl; // Line 51
成为
std::cout << g.innerChildren_[i].data_ << std::endl; // Line 51
因为您试图在未定义的地方使用运算符 <<
如果依赖第(27)行中定义的运算符,修改第(50)行以正确调用它,如下
for (size_t i = 0; i < g.innerChildren_.size(); ++i)
{
operator<< <T>(strm, g.innerChildren_[i]);
strm << std::endl;
}