我需要定义一个接受多个 3D 坐标作为参数的 C++ 模板。当这些坐标的所有维度都定义为单独的整数变量时,参数列表将变得非常长 - 3 个坐标需要 9 个参数,这使得模板难以使用。
因此,非常希望以使用编译时数组的方式声明模板。它们的默认参数也应该直接在模板声明的位置声明为值,而不是变量名。
经过一些实验,令我惊讶的是,我发现 GCC 13 将接受以下 C++ 程序std=c++20
:
#include <cstdio>
#include <array>
template <
std::array<int, 3> offset = {0, 0, 0}
>
struct Array
{
void operator() (int i, int j, int k)
{
printf("(%d, %d, %d)\n", i + offset[0], j + offset[1], k + offset[2]);
}
};
int main(void)
{
Array arr_default;
arr_default(0, 0, 0);
Array<{1, 1, 1}> arr;
arr(0, 0, 0);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
然而,clang 18 拒绝使用 …
c++ language-lawyer list-initialization braced-init-list c++-templates
示例代码是:
#include <unordered_map>
int main() {
std::unordered_map<int, std::pair<int, int>> map;
map.emplace(1, {1, 1});
return 0;
}
Run Code Online (Sandbox Code Playgroud)
哪里emplace()
有签名,如:
template <class... _Args>
pair<iterator, bool> emplace(_Args&&... __args);
Run Code Online (Sandbox Code Playgroud)
该gcc
说的功能expectes 0参数- 2提供的.该clang
说,函数需要1个参数- 2提供的.
我甚至不明白 - 这段代码有什么问题?
c++ variadic-functions compiler-bug c++11 list-initialization
假设我有以下代码:
#include <vector>
struct Foo
{
int tag = 0;
std::function<void ()> code;
};
int main()
{
std::vector<Foo> v;
}
Run Code Online (Sandbox Code Playgroud)
现在我想要Foo
使用特定的向量向量添加新项目,tag
而code
不是明确创建临时项目.这意味着我必须添加一个构造函数Foo
:
struct Foo
{
inline Foo(int t, std::function<void ()> c): tag(t), code(c) {}
int tag = 0;
std::function<void ()> code;
};
Run Code Online (Sandbox Code Playgroud)
现在我可以使用emplace_back
:
v.emplace_back(0, [](){});
Run Code Online (Sandbox Code Playgroud)
但是当我不得不再次这样做 - 第100次 - 使用新创建的结构时,我想:我不能使用大括号初始化器吗?像这样:
#include <vector>
struct Foo
{
int tag = 0;
std::function<void ()> code;
};
int main()
{
std::vector<Foo> v;
v.push_back(Foo{ 0, …
Run Code Online (Sandbox Code Playgroud) c++ initialization initializer-list c++11 list-initialization
鉴于以下内容:
#include <stdio.h>
class X;
class Y
{
public:
Y() { printf(" 1\n"); } // 1
// operator X(); // 2
};
class X
{
public:
X(int) {}
X(const Y& rhs) { printf(" 3\n"); } // 3
X(Y&& rhs) { printf(" 4\n"); } // 4
};
// Y::operator X() { printf(" operator X() - 2\n"); return X{2}; }
int main()
{
Y y{}; // Calls (1)
printf("j\n");
X j{y}; // Calls (3)
printf("k\n");
X k = {y}; // Calls (3) …
Run Code Online (Sandbox Code Playgroud) 我注意到当移动更适用时,std::vector 的聚合 列表初始化执行复制初始化。同时,多个 emplace_backs 做我想做的。
我只能想出这个编写模板函数的不完美解决方案init_emplace_vector
。不过,它仅适用于非显式单值构造函数。
template <typename T, typename... Args>
std::vector<T> init_emplace_vector(Args&&... args)
{
std::vector<T> vec;
vec.reserve(sizeof...(Args)); // by suggestion from user: eerorika
(vec.emplace_back(std::forward<Args>(args)), ...); // C++17
return vec;
}
Run Code Online (Sandbox Code Playgroud)
我真的需要使用 emplace_back来尽可能有效地初始化 std::vector 吗?
// an integer passed to large is actually the size of the resource
std::vector<large> v_init {
1000, // instance of class "large" is copied
1001, // copied
1002, // copied
};
std::vector<large> …
Run Code Online (Sandbox Code Playgroud) 从C++ 11开始,标准库容器和std::string
构造函数都采用初始化列表.这个构造函数优先于其他构造函数(甚至,正如@ JohannesSchaub-litb在评论中指出的那样,甚至忽略了其他"最佳匹配"标准).当将所有带括号()
的构造函数转换为其支撑版本时,这会导致一些众所周知的陷阱{}
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
void print(std::vector<int> const& v)
{
std::copy(begin(v), end(v), std::ostream_iterator<int>(std::cout, ","));
std::cout << "\n";
}
void print(std::string const& s)
{
std::cout << s << "\n";
}
int main()
{
// well-known
print(std::vector<int>{ 11, 22 }); // 11, 22, not 11 copies of 22
print(std::vector<int>{ 11 }); // 11, not 11 copies of 0
// more surprising
print(std::string{ 65, 'C' }); // AC, not 65 …
Run Code Online (Sandbox Code Playgroud) 请考虑以下代码:
class A {
private:
std::string s;
public:
A() = delete;
A(const A&) = delete;
A(A&&) = delete;
A(const std::string &a) : s(a) {}
};
Run Code Online (Sandbox Code Playgroud)
现在,我想初始化一个使用列表初始化的A数组.g ++(4.9.1)可以成功构建以下代码:
int main() {
A arr[2] = {{"a"}, {"b"}};
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但是,它失败了以下代码:
class Aggr {
private:
A arr[2];
public:
Aggr() : arr{{"a"}, {"b"}} {}
};
Run Code Online (Sandbox Code Playgroud)
错误消息是,
test.cc: In constructor ‘Aggr::Aggr()’:
test.cc:22:28: error: use of deleted function ‘A::A(A&&)’
Aggr() : arr{{"a"}, {"b"}} {}
^
test.cc:11:3: note: declared here
A(A&&) = delete;
^
Run Code Online (Sandbox Code Playgroud)
也就是说,list-initializer尝试调用move构造函数来初始化类中的数组.但是,该代码是由clang v3.5成功构建的,没有任何警告.所以,我想知道C++ …
struct A {
A(int) {}
};
struct B {
B(A) {}
};
int main() {
B b({0});
}
Run Code Online (Sandbox Code Playgroud)
构造出现b
以下错误:
In function 'int main()':
24:9: error: call of overloaded 'B(<brace-enclosed initializer list>)' is ambiguous
24:9: note: candidates are:
11:2: note: B::B(A)
10:8: note: constexpr B::B(const B&)
10:8: note: constexpr B::B(B&&)
Run Code Online (Sandbox Code Playgroud)
我期待B::B(A)
被召唤,为什么在这种情况下它是模棱两可的?
我有一个关于卷括号括号列表的不同含义的问题.
我知道C++ 03不支持C++ 11 initializer_list
.然而,即使没有-std=c++11
编译器标志,gcc 6.3 也会interpolate
使用以下代码正确初始化:
map<string, string> interpolate = { { "F", "a && b && c" }, { "H", "p ^ 2 + w" }, { "K", "H > 10 || e < 5" }, { "J", "F && !K" } };
Run Code Online (Sandbox Code Playgroud)
我被问到为什么这会起作用,我意识到我没有答案.这是一个Brace-Init-List,但我们从初始化标准容器的方式通常是通过initializer_list
.那么非C++ 11代码如何完成初始化呢?
考虑一下代码:
#include <atomic>
struct stru {
int a{};
int b{};
};
int main() {
// Doesn't work: std::atomic<stru> as({});
std::atomic<stru> as{{}};
}
Run Code Online (Sandbox Code Playgroud)
为直接初始化生成的错误消息如下:
prog.cc: In function 'int main()':
prog.cc:9:26: error: call of overloaded 'atomic(<brace-enclosed initializer list>)' is ambiguous
std::atomic<stru> as({});
^
In file included from prog.cc:1:0:
/opt/wandbox/gcc-7.2.0/include/c++/7.2.0/atomic:200:17: note: candidate: constexpr std::atomic<_Tp>::atomic(_Tp) [with _Tp = stru]
constexpr atomic(_Tp __i) noexcept : _M_i(__i) { }
^~~~~~
/opt/wandbox/gcc-7.2.0/include/c++/7.2.0/atomic:196:7: note: candidate: std::atomic<_Tp>::atomic(const std::atomic<_Tp>&) [with _Tp = stru] <deleted>
atomic(const atomic&) = delete;
^~~~~~
Run Code Online (Sandbox Code Playgroud)
我认为这两种形式在这种特殊情况下应该没有区别.为什么会这样?