У меня проблема с пониманием, почему следующий код не компилируется:
#include <iostream>
#include <vector>
#include <type_traits>
enum class Error
{
OK = 0,
NOK = 1
};
template <typename T, typename E = Error>
class Foo
{
public:
template <typename U = T,
std::enable_if_t<std::is_constructible<U>::value, bool> = true>
Foo(U&& other)
{
new (&value_) T(std::forward<U>(other));
}
private:
union
{
T value_;
E error_;
};
};
// usage:
Foo<std::int32_t> Check(std::vector<std::int32_t>& data)
{
// some code...
return data.at(0); // here is the problem
}
int main()
{
std::vector<std::int32_t> data{1, 2};
auto res = Check(data);
return 0;
}
и выдается ошибка:
could not convert ‘(& data)->std::vector::at(0)’ from ‘__gnu_cxx::__alloc_traits, int>::value_type’ {aka ‘int’} to ‘Foo’
Вот код: https://onecompiler.com/cpp/42n6d9sj8
Пока просто меняю это:
std::enable_if_t<std::is_constructible<U>::value, bool> = true>
к этому
std::enable_if_t<std::is_constructible<T, U>::value, bool> = true>
исправляет это.
Мое понимание:
Я возвращаюсь data.at(0)
, что будет int32_t
так U = int32_t
. В std::is_constructible<U>
будет проверяться, является ли int32_t
конструктивным (истина). Поэтому конструктор должен быть включен и должен быть создан экземпляр Foo...
Для второй версии
std::enable_if_t<std::is_constructible<T, U>::value, bool> = true>
Я проверяю, может ли T
быть построено из U
, оба являются типами int32_t
, поэтому я проверяю, может ли int32_t
быть построено из int32_t
, и это проходит, потому что это верно, поскольку U
совпадает с T
. Я не думаю, что это будет реально исправить, но у меня нет другого представления, как должен выглядеть правильный код в этом случае...
Проблема в том, что вы используете ссылку на пересылку, значение которой в параметре конструктора U
выводится как std::int32_t&
, а не std::int32_t
.
Итак, std::is_constructible<U>::value
терпит неудачу, потому что U
есть std::int32_t&
.
Вы можете решить/проверить это, удалив &&
из параметра функции.
template <typename T, typename E = Error>
class Foo
{
public:
template <typename U = T,
std::enable_if_t<std::is_constructible<U>::value, bool> = true>
//------v------------------------------------------------->removed && from here
Foo(U other)
{
new (&value_) T(std::move<U>(other));
}
//other code as before
};
Рабочая демо без пересылки ссылки .
Второй способ заставить эту работу работать — удалить ссылку из U
перед передачей ее в качестве аргумента в std::constructible
, как это сделано в ответе Теда.
Вы используете ссылку для пересылки, и std::is_constructible<int32_t&>::value
оценивается как false
. Вам необходимо std::remove_reference
в конструкторе SFINAE'd:
template <typename U = T,
std::enable_if_t<
std::is_constructible<std::remove_reference_t<U>>::value,
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
bool> = true>
Foo(U&& other) {
new (&value_) T(std::forward<U>(other));
}
Проблема в том, что вы используете ссылку пересылки, означающую, что
U
выводится какstd::int32_t&
, а неstd::int32_t
. Итак,std::is_constructible<U>::value
терпит неудачу, потому чтоU
естьstd::int32_t&
. Вы можете убедиться в этом, удалив&&
из параметра функции.