【问题标题】:Ignore a template spezialization and explicitly use the unspezialized template (std::vector<bool>)忽略模板特殊化并显式使用未特殊化的模板 (std::vector<bool>)
【发布时间】:2016-09-25 10:29:37
【问题描述】:

在处理一些旧代码时,它遇到了一个std::unique_ptr&lt;bool&gt;,用于存储一些布尔值(在类构造函数中分配并用作数组)。

当我试图用std::vector&lt;bool&gt; 替换它时,我遇到了一个问题,我不得不调用一个库函数,该函数接受一个计数和一个指向第一个布尔值的指针 (const bool*):有一个模板专门化std::vector&lt;bool&gt; 将 8 个布尔值压缩到一个字节中,因此如果不先解压缩,就无法获得指向数据的 bool* 指针。

我已经通过 Google 搜索或 StackOverflow 文章 C++11 vector<bool> performance issue (with code example) 找到了一些解决方案,但它们都不适合我(即使用包含布尔值的结构会起作用,但这会使我尝试简化的代码更加复杂; std::valarray 不提供 data() 成员)

还有一篇文章“How to prevent specialization of std::vector<bool>”,但所有解决方案都只是变通方法,我不相信“bool 和 unsigned char 通常自己占用相同数量的内存”这句话(在compiler error when pointing to an element of std::vector<bool>? 中提到)

我还检查了Alternative to vector<bool>,但我们没有在我们的解决方案中使用 boost,我也不愿意添加这个依赖项以供一次性使用。

我的问题是:有没有办法忽略模板特殊化并显式使用未特殊化的模板作为类型?

例如

#include <iostream>

template<class T>
class MyTemplate
{
public:
  static const int Value = 0;
}

template<>
class MyTemplate<double>
{
public: 
  static const int Value = 1;
};

int main(int argc, char **argv)
{
  // How can I make MyTemplate<double> ignore the spezialization and output 0?
  std::cout << "0==" << MyTemplate<double>::Value << std::endl;

  return 0;
}

【问题讨论】:

  • 将书包装在某个结构中并从那里继续如何?
  • 那是“标准”解决方案,但要使用库调用,我需要一个 reinterpret_cast 并且在读取或写入项目时,我需要访问结构的值成员,否则我必须在结构中实现自动装箱/拆箱,这使得它变得复杂。
  • 拥有布尔值数组的方法有很多,但绕过vector&lt;bool&gt; 特化不是其中之一。
  • 等价于std::unique_ptr&lt;bool&gt; 将是unique&lt;bool&gt;std::vector&lt;bool&gt; 将用于 std::unique_ptr&lt;bool[]&gt;...
  • 是的,这就是我要更改此代码的原因之一。 (你确定你打算写unique&lt;bool&gt; 而不仅仅是bool?)

标签: c++ templates pointers c++11 vector


【解决方案1】:

答案是否定的,因为无法保证甚至存在非专业模板。

特别是,编译器对所有内置类型进行专门化是非常合理的。因此,您通常会看到编译时调度:vector&lt;T&gt; 转发到 __VectorImplBuiltIn&lt;T&gt;,只要 T 是内置类型,而 __VectorImplBuiltIn&lt;T&gt; 反过来专门针对每个单独的情况。所以vector&lt;float&gt; 可以使用 AVX 一次复制 4 个浮点数,而 vector&lt;bool&gt; 被打包。

现在没有可移植的方式来命名特定的实现类(它实际上是一个实现细节)并且非专业化的__VectorImplBuiltIn&lt;T&gt; 甚至没有通用实现,因为编译器供应商显然知道所有内置类型。

在此方案中,“正常”vector&lt;T&gt; 将扩展为 __VectorImplClassType&lt;T&gt;,这对于 T==bool 可能不适用,因为 bool 没有构造函数。

因此,std::vector&lt;T&gt; 以区分有无构造函数的类型开始的完全合理的方案将使您的想法变得不可能。因此,ISO C++ 标准不允许你想要的也就不足为奇了。

【讨论】:

    【解决方案2】:

    您可以创建一个看起来与bool 行为相同的包装结构,因此不需要修改期望它是bool 的代码:

    struct B {
        bool val;
        B() {}
        B(bool val): val(val) {}
        operator bool&() { return val; }
        operator const bool&() const { return val; }
        bool* operator&() { return &val; }
        const bool* operator&() const { return &val; }
    };
    

    那么以下都是可能的:

    B a = true, b(0);
    a || 2 == 1;
    5 ^ a;
    const bool& x = a;
    bool* y = &a;
    a &= b;
    !a;
    

    【讨论】:

    • 这是我在问题中写到的stackoverflow.com/questions/36932684/… 的解决方案。但是它给代码增加了很多复杂性,并且仍然需要 reinterpret_cast 来调用库。
    • @AndreasH。我在那个链接中没有看到类似的东西。它如何需要 reinterpret_cast?
    • 调用需要bool * 的库函数时,您可以在ideone.com/exMf6X 看到
    • @AndreasH。您可以将该部分替换为 libraryFunction(&amp;v[0]),假设它不在库中。
    • 这可行,但是 - 对不起 - 这不是我的问题的答案 '有没有办法忽略模板特殊化并明确使用未特殊化的模板作为类型?'
    【解决方案3】:

    这并不是对您问题的确切答案,尽管它可能是对您的问题的答案。

    您说您正在尝试使用将bool * 作为参数的库,并且您没有使用std::valarray,因为它没有data()

    但是以下方法不起作用吗?

    #include <iostream>
    #include <valarray>
    
    using namespace std;
    
    void doStuff(bool *x) {
        *x = true;
    }
    
    int main() {
        valarray<bool> v = {false,false,false,false,false,false};
    
        doStuff(&v[2]);
    
        for (bool b : v) {
            cout << b << endl;
        }
    
        //if you *really* want data(), &v[0] does the same
        return 0;
    }
    

    Live Demo

    注意valarray is guaranteed to have contiguous memory


    另一种方法是创建一个替换容器,一个名为 SimpleVector 的示例被指定为 here,它只需要添加一个简单的头文件,并且代码的海报参考了 Mark Allen Weiss 的一本书。

    在该实现中,begin() 的行为与 data() 相同。

    虽然代码是c++11之前的代码,所以它没有实现&amp;&amp;的构造函数。

    【讨论】:

    • 库函数接受一个计数和一个指向第一个布尔值的指针。只要valarray&lt;bool&gt; 将其数据作为布尔值存储在连续块中,&v[0] 就可以工作,但此行为取决于实现。
    • @AndreasH。实际上它可以保证工作,我用reference编辑了我的帖子。
    • 感谢您的参考。这解决我的问题的方法,但恐怕它不是我问题的可接受答案。很抱歉,但我必须接受 MSalters 的回答,因为它与问题相符,但至少我可以给你一个赞成票。
    猜你喜欢
    • 1970-01-01
    • 2015-03-04
    • 2021-12-14
    • 2016-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多