【问题标题】:C++ Unable to Perform Flow Control Based on STL Container's TypeC++ 无法执行基于 STL 容器类型的流控制
【发布时间】:2014-12-30 21:08:24
【问题描述】:

以下是一个类的示例,它遍历 STL 容器并打印出每个元素,并为 main() 附加了初始化代码。

似乎编译器在编译时解析了整个 ContainerPrinterClass,并在到达传入的 STL 容器之一中的元素不支持的行时抛出错误,例如 element.first/element.second 类型为 @987654323 @ 在向量中。然而,它甚至不应该进入那个条件,因为流控制明确声明只输入它if (std::is_same<T, std::map<int, std::string>>::value) 是真的。

当映射被传递到打印函数并且编译器在else 条件中标记错误时,它试图打印元素,这是一个 std::pair 并且不能。但它甚至不应该处于 else 条件,因为如果传递了一个 map,执行将转到 if 条件并完全跳过 else

我还尝试尝试替换 (typeid(*container) == typeid(std::map<int, std::string>))dynamic_cast 来检查元素是否为具有相同结果的 std::pair,其中编译器会解析打印机函数中的所有条件(甚至是不正确的条件)对于特定类型)在编译时。

那么,有谁知道如何提供类似于 Java 的 instanceOf 的这种模板/通用功能,并且不让编译器在不应该针对该特定类型进入的条件下盲目地解析代码?


// 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>          ]

【问题讨论】:

  • 为什么不为std::map&lt;int, std::string&gt; 提供printElementsInContainer 的专业化?
  • 请注意,for each 是 C++ 的 Visual C++ 扩展,不是标准的。如果您正在寻找 C++11 容器迭代循环结构,它是 for (auto &amp; element : *container)。 (但是你为什么不通过引用传递你的容器呢?)
  • 编译时,所有代码路径都必须包含有效代码,因此当函数模板专门用于std::vector 时,尝试访问成员firstsecond 不会编译。您可以根据 std::is_same 产生的 true_type/false_type 标记调度到分别处理 mapvector 情况的实现。最简单的解决方案是使用pretty printer
  • if 不是条件代码编译工具。

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


【解决方案1】:

当一个函数模板被实例化时,它的所有代码都需要是正确的代码。当您想要执行取决于模板参数功能的代码时,您需要利用相关功能分派到合适的功能模板。例如,您可以像这样重写您的代码:

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&lt;...&gt; 的使用使相应的类型有条件。

【讨论】:

  • 谢谢你们的回答,他们都是有效的选择,但是我必须选择 Dietmar 的答案作为最佳答案,因为他的答案指向 std::enable,这正是我想要的又如文档中所述,“这对于在不满足特定条件时在编译时隐藏签名很有用”!
  • @sharik:我认为无论你站在哪里,C++ 之类的东西都需要学习很多东西:我从事 C++ 编程已有近 25 年了,现在我正在参加 C++ 委员会会议 - 。 ..我还在学习! (今天我的主要课程来自std::void_t&lt;...&gt; proposal)。
【解决方案2】:

这失败了,因为即使 std::is_same 将为任何一个特定的 T 返回相同的值,条件的另一个分支仍然需要编译。

这意味着当您使用std::vector&lt;int&gt; 调用时,即使std::is_same 表达式的计算结果为false,if 主体仍然需要正确,并且int 没有first 成员。

这同样适用于地图的情况; else 主体仍然需要编译,因为没有接受 std::pair&lt;const int, std::string&gt;operator&lt;&lt;

使用重载、模板特化或 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&lt;int&gt;

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;
    }
}

【讨论】:

    猜你喜欢
    • 2011-06-22
    • 2013-08-19
    • 2014-03-10
    • 2019-07-02
    • 1970-01-01
    • 2010-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多