Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
List-initialization of an object or reference of type T is defined as follows:Тут явно какая-то ошибка, потому что до нижнего пункта в случае ссылок мы никогда не дойдем, а предпоследний пункт говорит нам выдавать compilation error на неконстантные lvalue ссылки, а все остальные инициализировать путем обязательного создания временной переменной и привязки её к ссылке, что тоже весьма странно.
- If the initializer list has no elements and T is a class type with a default constructor...
- Otherwise, if T is an aggregate...
- Otherwise, if T is a specialization of std::initializer_list<E>...
- Otherwise, if T is a class type...
- Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is list-initialized, and the reference is bound to that temporary. [ Note: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type. —end note ]
- Otherwise, if the initializer list has a single element, the object or reference is initialized from that element;...
- ...
- ...
- Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element; if
a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.- Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is copy-list-initialized or direct-list-initialized, depending on the kind of initialization for the reference, and the reference is bound to that temporary. [ Note: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type. —end note ]
- ...
struct A {
int x;
double y;
const char* z;
};
For the components affected by this subclause that declare an allocator_type, objects stored in these components shall be constructed using the allocator_traits<allocator_type>::construct function and destroyed using the allocator_traits<allocator_type>::destroy function (20.6.8.2). These functions are called only for the container’s element type, not for internal types used by the container. [ Note: This means, for example, that a node-based container might need to construct nodes containing aligned buffers and call construct to place the element into the buffer. —end note]а allocator_traits<allocator_type>::construct в свою очередь вызывает метод construct переданного ей аллокатора, если этот метод определен. То есть нам всего лишь нужно создать свой аллокатор с методом construct, который будет делать инициализацию нужным нам способом. На практике все сложнее, путем небольшим плясок с бубном мне удалось завести это для вектора:
#include <memory>
#include <vector>
#include <string>
#include <iostream>
#include <cmath>
template<typename T>
struct init_list_allocator : public std::allocator<T> {
template<typename... Args>
void construct(T* p, Args&&... args)
{ ::new((void *)p) T{std::forward<Args>(args)...}; }
// Чиним инициализацию копированием
void construct(T* p, T& copy_construct_arg)
{ std::allocator<T>::construct(p, copy_construct_arg); }
void construct(T* p, const T& copy_construct_arg)
{ std::allocator<T>::construct(p, copy_construct_arg); }
void construct(T* p, const T&& copy_construct_arg)
{ std::allocator<T>::construct(p, copy_construct_arg); }
void construct(T *p, T&& move_construct_arg)
{ std::allocator<T>::construct(p, move_construct_arg); }
// Без этого не работает, потому что вместо переданного типа аллокатора
// реализация зачем-то использует Allocator::rebind<T>::other
template<typename U>
struct rebind {
using other = init_list_allocator<U>;
};
};
template<class T>
using improved_vector = std::vector<T, init_list_allocator<T>>;
struct A {
int x;
double y;
const char* z;
};
int main()
{
using namespace std;
vector<string> strings;
improved_vector<A> v;
for (int i = 0; i < 21; ++i) {
strings.emplace_back(to_string(i*i));
v.emplace_back(i, sqrt(i), strings.back().c_str());
};
for (const auto& elem : v)
cout << elem.x << ' ' << elem.y << ' ' << elem.z << '\n';
}
template<typename _Tp>
constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{
return static_cast<_Tp&&>(__t);
}
template<typename _Tp>
constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
{
static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
" substituting _Tp is an lvalue reference type");
return static_cast<_Tp&&>(__t);
}
template< class... Args >
void some_func( Args&&... args )
{
T some_object(std::forward<Args>(args)...);
}
Подскажите, пожалуйста.
Раз эта иделаьная передача способна конструировать объекты in_place, то почему же не компилируется такой код?
class driver_t {
public:
driver_t(int) noexcept { std::cout << "ctor" << std::endl; };
driver_t(const driver_t&) = delete;
driver_t(driver_t&&) =delete;
driver_t& operator=(const driver_t&) = delete;
};
int main() {
v.emplace_back(driver_t{1});
return 0;
};
Я ожидаю вызова только конструктора. И никаких копирований или перемещений.
Идеальная передача и универсальные ссылки в C++