I can concisely (with braces) initialize 5 out of 6 of the following cases:
of copyable | of move-only | |
---|---|---|
array | YES | YES |
std::array | YES | YES |
std::vector | YES | NO |
The one case that doesn't seem to work is attempting initialization of a std::vector of move-only objects; that fails to compile, with message such as: "error: call to implicitly-deleted copy constructor of 'std::unique_ptr'".
Why is this? And is there an alternative initialization syntax that works for that case?
The following program demonstrates.
/*
clang++ -std=c++14 -W -Wall -Werror question.cc -o question
clang++ -std=c++17 -W -Wall -Werror question.cc -o question
clang++ -std=c++20 -W -Wall -Werror question.cc -o question
clang++ -std=c++2b -W -Wall -Werror question.cc -o question
g++ -std=c++14 -W -Wall -Werror question.cc -o question
g++ -std=c++17 -W -Wall -Werror question.cc -o question
g++ -std=c++20 -W -Wall -Werror question.cc -o question
g++ -std=c++2b -W -Wall -Werror question.cc -o question
*/
#include <array>
#include <memory>
#include <vector>
int main(int, char **) {
{
// I can initialize an array, std::array, or std::vector of
// copyable objects concisely as follows:
std::shared_ptr<int> a[] = {std::make_shared<int>(42)};
std::shared_ptr<int> b[] {std::make_shared<int>(42)};
std::array<std::shared_ptr<int>, 1> c = {std::make_shared<int>(42)};
std::array<std::shared_ptr<int>, 1> d {std::make_shared<int>(42)};
std::vector<std::shared_ptr<int>> e = {std::make_shared<int>(42)};
std::vector<std::shared_ptr<int>> f {std::make_shared<int>(42)};
}
{
// And I can also initialize an array or std::array (but not a std::vector)
// of move-only (non-copyable) objects similarly:
std::unique_ptr<int> a[] = {std::make_unique<int>(42)};
std::unique_ptr<int> b[] {std::make_unique<int>(42)};
std::array<std::unique_ptr<int>, 1> c = {std::make_unique<int>(42)};
std::array<std::unique_ptr<int>, 1> d {std::make_unique<int>(42)};
// But if I try to do the same with std::vector of move-only
// objects, it fails to compile.
// clang++:
// "error: call to implicitly-deleted copy constructor
// of 'std::unique_ptr<int>'"
// g++ (c++14, c++17):
// "error: static assertion failed: result type must be constructible
// from input type"
// g++ (g++20, g++2b):
// "error: use of deleted function
// ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(
// const std::unique_ptr<_Tp, _Dp>&)
// [with _Tp = int; _Dp = std::default_delete<int>]’
#if 1 // (hard code to 1 for error, 0 for successful compile)
std::vector<std::unique_ptr<int>> e = {std::make_unique<int>(42)};
std::vector<std::unique_ptr<int>> f {std::make_unique<int>(42)};
#endif
// The most concise workaround I have found is as follows.
// (This would not work if I wanted to make the vector const.)
// Notice that initializer-list initialization does succeed,
// as long as the initializer list is empty!
std::vector<std::unique_ptr<int>> e_workaround = {};
e_workaround.push_back(std::make_unique<int>(42));
std::vector<std::unique_ptr<int>> f_workaround {};
f_workaround.push_back(std::make_unique<int>(42));
}
return 0;
}
vector v(from_range, views::single(make_unique<int>(42)) | views::as_rvalue);
:) – Syl