以下是一个类的示例,该类遍历STL容器并打印出每个元素,并为main()附加了初始化代码。
似乎编译器在编译时解析整个ContainerPrinterClass,并且当它到达一个传入的STL容器中的元素不支持的行时抛出错误,例如类型为element.first/element.second
向量中的int
。然而,它不应该甚至进入那个条件,因为流量控制明确指出只输入它if (std::is_same<T, std::map<int, std::string>>::value)
是真的。
当映射传递给print函数并且编译器在else
条件下标记错误时,反之亦然,它尝试打印元素,这是一个std :: pair并且可以'吨。但它甚至不应该处于其他条件,因为如果传递了一个映射,则执行转到if
条件并完全跳过else
。
我还试图替换(typeid(*container) == typeid(std::map<int, std::string>))
和dynamic_cast
以检查元素是否是具有相同结果的std::pair
,其中编译器解析打印机中的所有条件在编译时函数(即使是特定类型的错误函数)。
那么有没有人知道如何提供类似于Java instanceOf
的这种模板/通用功能,并且编译器不会在不应该针对该特定类型的条件下盲解析代码?< / p>
// the printer class
class ContainterPrinterClass {
public:
ContainterPrinterClass() {};
template<class T> void printElementsInContainer(T *container) {
// if container type is map, print key and value
if (std::is_same<T, std::map<int, std::string>>::value) {
for each (auto element in *container) {
std::cout << "key: " << element.first << " val: " << element.second << std::endl;
}
} else {
// else if container is a vector, print just element
for each (auto element in *container) {
std::cout << "val: " << element << std::endl;
}
}
}
};
// in main()
ContainterPrinterClass containerPrinterClass;
std::vector<int> vectorOfInts = {1};
containerPrinterClass.printElementsInContainer<std::vector<int>>(&vectorOfInts);
std::map<int, std::string> mapOfStrings = {std::pair<int, std::string>(2, "Two")};
containerPrinterClass.printElementsInContainer<std::map<int, std::string>>(&mapOfStrings);
然而,它不会编译并发出以下错误:
1>e:\workbench\projects\visualstudio_workspaces\tuts\testbed1\basic_app\basic_app.cpp(503): error C2228: left of '.first' must have class/struct/union
1> type is 'int'
1> e:\workbench\projects\visualstudio_workspaces\tuts\testbed1\basic_app\basic_app.cpp(747) : see reference to function template instantiation 'void ContainterPrinterClass::printElementsInContainer<std::vector<int,std::allocator<_Ty>>>(T *)' being compiled
1> with
1> [
1> _Ty=int
1> , T=std::vector<int,std::allocator<int>>
1> ]
1>e:\workbench\projects\visualstudio_workspaces\tuts\testbed1\basic_app\basic_app.cpp(503): error C2228: left of '.second' must have class/struct/union
1> type is 'int'
1>e:\workbench\projects\visualstudio_workspaces\tuts\testbed1\basic_app\basic_app.cpp(508): error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'std::pair<const _Kty,_Ty>' (or there is no acceptable conversion)
1> with
1> [
1> _Kty=int
1> , _Ty=std::string
1> ]
1> c:\visualstudio2013pro\vc\include\ostream(498): could be 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(std::basic_streambuf<char,std::char_traits<char>> *)'
1> c:\visualstudio2013pro\vc\include\ostream(478): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(const void *)'
1> c:\visualstudio2013pro\vc\include\ostream(458): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(long double)'
1> c:\visualstudio2013pro\vc\include\ostream(438): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(double)'
1> c:\visualstudio2013pro\vc\include\ostream(418): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(float)'
1> c:\visualstudio2013pro\vc\include\ostream(397): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(unsigned __int64)'
1> c:\visualstudio2013pro\vc\include\ostream(377): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(__int64)'
1> c:\visualstudio2013pro\vc\include\ostream(356): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(unsigned long)'
1> c:\visualstudio2013pro\vc\include\ostream(336): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(long)'
1> c:\visualstudio2013pro\vc\include\ostream(316): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(unsigned int)'
1> c:\visualstudio2013pro\vc\include\ostream(291): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(int)'
1> c:\visualstudio2013pro\vc\include\ostream(271): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(unsigned short)'
1> c:\visualstudio2013pro\vc\include\ostream(237): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(short)'
1> c:\visualstudio2013pro\vc\include\ostream(217): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(std::_Bool)'
1> c:\visualstudio2013pro\vc\include\ostream(210): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(std::ios_base &(__cdecl *)(std::ios_base &))'
1> c:\visualstudio2013pro\vc\include\ostream(203): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(std::basic_ios<char,std::char_traits<char>> &(__cdecl *)(std::basic_ios<char,std::char_traits<char>> &))'
1> c:\visualstudio2013pro\vc\include\ostream(197): or 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(std::basic_ostream<char,std::char_traits<char>> &(__cdecl *)(std::basic_ostream<char,std::char_traits<char>> &))'
1> c:\visualstudio2013pro\vc\include\ostream(699): or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,const char *)'
1> c:\visualstudio2013pro\vc\include\ostream(746): or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char)'
1> c:\visualstudio2013pro\vc\include\ostream(784): or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,const char *)'
1> c:\visualstudio2013pro\vc\include\ostream(831): or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char)'
1> c:\visualstudio2013pro\vc\include\ostream(957): or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,const signed char *)'
1> c:\visualstudio2013pro\vc\include\ostream(964): or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,signed char)'
1> c:\visualstudio2013pro\vc\include\ostream(971): or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,const unsigned char *)'
1> c:\visualstudio2013pro\vc\include\ostream(978): or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,unsigned char)'
1> c:\visualstudio2013pro\vc\include\ostream(988): or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<char,std::char_traits<char>,std::pair<const _Kty,_Ty>>(std::basic_ostream<char,std::char_traits<char>> &&,const std::pair<const _Kty,_Ty> &)'
1> with
1> [
1> _Kty=int
1> , _Ty=std::string
1> ]
1> c:\visualstudio2013pro\vc\include\ostream(1026): or 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,const std::error_code &)'
1> while trying to match the argument list '(std::basic_ostream<char,std::char_traits<char>>, std::pair<const _Kty,_Ty>)'
1> with
1> [
1> _Kty=int
1> , _Ty=std::string
1> ]
1> e:\workbench\projects\visualstudio_workspaces\tuts\testbed1\basic_app\basic_app.cpp(750) : see reference to function template instantiation 'void ContainterPrinterClass::printElementsInContainer<std::map<int,std::string,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>>>(T *)' being compiled
1> with
1> [
1> _Kty=int
1> , _Ty=std::string
1> , T=std::map<int,std::string,std::less<int>,std::allocator<std::pair<const int,std::string>>>
1> ]
答案 0 :(得分:2)
当实例化函数模板时,其所有代码都需要是正确的代码。当您想要执行依赖于模板参数功能的代码时,您需要利用相关能力将其分派到合适的功能模板。例如,您可以像这样重写代码:
template<class T>
typename std::enable_if<std::is_same<T, std::map<int, std::string>>::value>::type
printElementsInContainer(T *container) {
for (auto&& element: *container) {
std::cout << "key: " << element.first << " val: " << element.second << '\n';
}
}
template<class T>
typename std::enable_if<!std::is_same<T, std::map<int, std::string>>::value>::type
printElementsInContainer(T *container) {
for (auto&& element: *container) {
std::cout << "val: " << element << '\n';
}
}
在某种程度上,使用std::enable_if<...>
会使相应的条件成为条件。
答案 1 :(得分:1)
这会失败,因为即使std::is_same
将为任何一个特定T
返回相同的值,仍然需要编译条件的另一个分支。
这意味着当您使用std::vector<int>
进行通话时,即使std::is_same
表达式的计算结果为false,if
正文仍需要正确,而int
不能正确我有一个first
成员。
同样适用于地图案例; else
正文仍然需要编译,但它没有,因为没有operator<<
接受std::pair<const int, std::string>
。
使用重载,模板特化或SFINAE仅激活正确的代码。以下是使用专业化的示例:
template<class T> void printElementsInContainer(T *container) {
for each (auto element in *container) {
std::cout << "val: " << element << std::endl;
}
}
template<> void printElementsInContainer(std::map<int, std::string> *container) {
for each (auto element in *container) {
std::cout << "key: " << element.first << " val: " << element.second << std::endl;
}
}
我会考虑将重载与主函数相结合;你可以将迭代的逻辑(对所有容器都是一样的)与显示元素的逻辑(取决于每个容器中元素的类型)分开。
此代码适用于任何std::map
(只要键和值可以写入流),还可以std::multimap
,std::unordered_map
或一般任何标准(或自定义)元素为std::pair
的容器。它也适用于任何元素不是成对但可以写入流的容器,例如std::vector<int>
。
template<class TStream, class TElement>
void writeElementTo(TStream & stream, TElement const & element)
{
stream << "val: " << element;
}
template<class TStream, class TKey, class TValue>
void writeElementTo(TStream & stream, std::pair<TKey, TValue> const & element)
{
stream << "key: " element.first << "value: " << element.second;
}
template<class T> void printElementsInContainer(T *container) {
for each (auto element in *container) {
writeElementTo(std::cout, element);
std::cout << std::endl;
}
}