cppcheck vs clang整洁:显式构造函数initializer_list

cppcheck vs clang-tidy : explict constructor initializer_list

本文关键字:构造函数 initializer list vs clang 整洁 cppcheck      更新时间:2023-10-16

当我运行工具clang-tidy-3.8和cppcheck-1.72时,在以下代码下:

#include <initializer_list>
#include <string>
#include <iostream>
using string_list = std::initializer_list<std::string>;
class Foo {
    public:
    explicit Foo(const string_list& strings) {
        for (const auto& ss : strings) {
            std::cout << ss << std::endl;
        }
    }
};

clang-tidy-3.8输出:

$>clang整洁-checks='*main.cpp--std=c++11

警告:初始值设定项列表构造函数不应声明为显式[google显式构造函数]显式Foo(const-string_list&string)

但是,如果我删除关键字显式,则cppcheck-1.72报告:

$>cppcheck-main.cpp--language=c++--std=c++11--enable=all

(style)类"Foo"有一个具有1个非显式参数的构造函数。

我在谷歌Cpp指南上阅读:

不能用单个参数调用的构造函数通常应该省略显式。采用单个std::initializer_list参数的构造函数也应该省略显式,以便支持复制初始化(例如,MyType m={1,2};)。

哪种工具是正确的根据C++标准?

正如@KerrekSB所说,这取决于你想要执行的构建风格。

如果您使初始值设定项列表构造函数explicit,则

  • 您不允许YourType A = {a, b, c};
  • 但只允许YourType A({a, b, c});(或YourType A{{a, b, c}};)(我认为有些编译器接受YourType A{a, b, c},但我发现它不一致。)

如果不将其标记为explict,两种情况都是允许的。

有些人主张永远不要在(类的)构造函数中使用=(甚至不用于初始值设定项列表参数),因此这最终是您通过标记explicit来强制执行的样式。

标记explicit的另一个重要副作用是,您必须考虑到,您将无法将原始初始值设定项列表作为函数参数来传递,以代替构造的对象(这可能是限制性的,但可能是进一步样式考虑的一部分)。例如fun(arg1, arg2, {a, b, c})fun(arg1, arg2, YourType({a, b, c}))

还要注意,例如,std::vector::vector(std::initializer_list)(或任何其他std容器)是而不是标记为explicit


我的经验法则是,当右手边可以表示为"0"时,我允许构造函数中的CCD_;忠实地";具有构造类型并且计算复杂度低(例如小于O(N log N)或O(N^2))。IMO没有多少情况下可以通过初始值设定项列表来实现这一点。我遇到的唯一例子是1)数组或列表(包括std::vector)的一些转世2)无序线性容器(但IMO不包括有序容器)。3) 多维数组(嵌套初始值设定项列表)。4) 元组(尽管该语言中存在非齐次初始值设定项列表)。

(有了这个规则,我认为std::set不显式是一个错误,因为std::set将在幕后重新排序)。


在实践中,我所做的是使用对cppcheck警告进行内联抑制的注释,我觉得注释对于任何隐式单参数构造函数来说都是必要的。

    // cppcheck-suppress noExplicitConstructor ; because human-readable explanation here
    YourType(std::initializer_list<value_type> il){...}

并使用选项CCD_ 20运行CCD_。

(参见http://cppcheck.sourceforge.net/manual.pdf#page=19)