cppcheck vs clang-tidy:显式构造函数initializer_list
cppcheck vs clang-tidy : explict constructor initializer_list
当我运行工具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
warning: initializer-list constructor should not be declared explicit [google-explicit-constructor]
explicit Foo(const string_list& strings)
但是,如果我 删除 关键字 explicit,cppcheck-1.72 报告:
$ > cppcheck main.cpp --language=c++ --std=c++11 --enable=all
(style) Class 'Foo' has a constructor with 1 argument that is not explicit.
我在 Google Cpp Guide 阅读:
Constructors that cannot be called with a single argument should usually omit explicit. Constructors that take a single std::initializer_list parameter should also omit explicit, in order to support copy-initialization (e.g. 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)
(或任何其他标准容器)未 标记为 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){...}
和 运行 cppcheck
选项 --inline-supp
.
当我运行工具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
warning: initializer-list constructor should not be declared explicit [google-explicit-constructor] explicit Foo(const string_list& strings)
但是,如果我 删除 关键字 explicit,cppcheck-1.72 报告:
$ > cppcheck main.cpp --language=c++ --std=c++11 --enable=all
(style) Class 'Foo' has a constructor with 1 argument that is not explicit.
我在 Google Cpp Guide 阅读:
Constructors that cannot be called with a single argument should usually omit explicit. Constructors that take a single std::initializer_list parameter should also omit explicit, in order to support copy-initialization (e.g. 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)
(或任何其他标准容器)未 标记为 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){...}
和 运行 cppcheck
选项 --inline-supp
.