【问题标题】:cppcheck vs clang-tidy : explict constructor initializer_listcppcheck vs clang-tidy:显式构造函数initializer_list
【发布时间】:2016-08-04 21:34:25
【问题描述】:

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

#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-tidy -checks='*' main.cpp -- -std=c++11

警告:初始化列表构造函数不应被显式声明 [google-explicit-constructor] 显式 Foo(const string_list& 字符串)

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

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

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

我在Google Cpp Guide阅读:

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

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

【问题讨论】:

  • “正确”是什么意思?您希望如何使用您的构造函数?
  • clang-tidy (3.8)that a constructor with only 1 argument has to be explicitthat a constructor with an initializer-list should not be explicit 都有测试,但 cppcheck (1.73) 没有测试 initializer-list。所以 clang-tidy 显式放宽了 initializer-list 的单参数规则。
  • 是来自C++11标准,这个宽松的规则吗?
  • @KerrekSB,“正确”的意思(对我来说),它遵循 C++ 标准。构造函数只是一个示例,但是,我可以使用相同的实现创建一个类似的对象来接收真实对象。在这种情况下,我不知道这只是一个样式规则,由谷歌(clang-tidy)还是一个 C++ 规则(cppcheck bug)。
  • C++ 标准允许使用或不使用explicit 关键字来声明任何构造函数。您的代码的两种变体都同样符合。这些工具不检查是否符合标准(编译器会这样做),而是检查样式指南 - 显然,不同的。你一直说cppcheck 强制执行你所谓的“C++ 规则”——但请注意它的消息是如何清楚地标记为(style)

标签: c++ c++11 clang static-code-analysis cppcheck


【解决方案1】:

正如@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)(或任何其他标准容器)没有标记为explicit


我的经验法则是,当右侧可以用构造类型“忠实”表示并且计算复杂度低(例如小于 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){...}

并使用选项--inline-supp 运行cppcheck

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-26
    • 1970-01-01
    • 2016-10-07
    • 1970-01-01
    • 1970-01-01
    • 2021-02-12
    • 2015-01-31
    • 2016-05-31
    相关资源
    最近更新 更多