C ++无法基于STL容器类型执行流控制

时间:2014-11-04 03:36:47

标签: c++ visual-c++ c++11 stl

以下是一个类的示例,该类遍历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>          ]

2 个答案:

答案 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::multimapstd::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;
    }
}