Шаблоны удаления SFINAE, Constexpr и Function: можно ли разделить объявление и определение?
У меня есть проект C++14, который я разрабатываю на CLion 2016.3.4, и один фрагмент кода давал мне ошибки проверки. Я создал минимальный код для воспроизведения проблемы:
#include <iostream>
#include <type_traits>
#include <system_error>
using error_id_type = int;
template <typename T> using enable_if_condition_enum_t =
typename std::enable_if<std::is_error_condition_enum<T>::value, T>::type;
// Declaration
template <typename T, typename = enable_if_condition_enum_t<T>>
constexpr error_id_type error_enum_to_int(T elem) noexcept;
// Definition
template <typename T, typename = enable_if_condition_enum_t<T>>
constexpr error_id_type error_enum_to_int(T elem) noexcept {
return static_cast<error_id_type>(elem);
};
int main(void) {
error_id_type condition = error_enum_to_int(std::errc::owner_dead); // inspection error here
switch (condition) {
case error_enum_to_int(std::errc::address_in_use): break; // inspection error here
default: break;
}
std::cout << condition << std::endl;
return 0;
}
CLion дает мне Call to "error_enum_to_int" is ambiguous
за каждое появление звонка error_enum_to_int
, Есть ли что-то не так с этим видом использования?
Некоторые вещи я пробовал, но это не совсем исправляет ИМХО:
- Удалите объявление и оставьте только определение (работает, но я хотел сохранить их в разных местах в одном и том же модуле компиляции, если это возможно);
- Удалите аргумент шаблона SFINAE (работает, но тогда он будет работать для каждого типа, и это не мое намерение);
- Заменить
T
аргументenable_if_condition_enum_t<T>
(не работает, дает мне новый набор ошибок компиляции и проверки).
Кроме того, код компилируется и работает очень хорошо, без каких-либо исправлений, на g++ (GCC) 6.3.1 20170306
, К сожалению, у меня нет доступа к другому компилятору, чтобы проверить это прямо сейчас, но я предполагаю, что это стандартный переносимый C++11.
Конечно, всегда есть возможность static_cast<some_enum_class>(some_int)
, но я хочу знать, что конкретно может быть не так с этим фрагментом кода.
Итак, мой вопрос: это ошибка моей IDE, действительно ли есть какой-то угловой случай языка, о котором я не знаю, или я действительно делаю что-то глупое здесь (:D)?
Мои рассуждения
Пожалуйста, поправьте меня, если я ошибаюсь.
Объявление само по себе не является определением, даже если это шаблон функции. Шаблон функции сам по себе не является функцией до тех пор, пока он не будет создан. Несмотря на это, компилятор должен видеть, что есть два разных вхождения одного и того же шаблона функции, и не путать одно с другим, если оба присутствуют в одном и том же модуле компиляции / перевода. Насколько я понимаю, CLion (или, возможно, статический анализатор clang) каким-то образом рассматривает объявление как определение и интерпретирует их как разные вещи. И так как оба являются шаблонами, становится неясным, какой из них создавать.
Обратите внимание, что и объявление, и определение присутствуют в одном и том же модуле компиляции. Также, constexpr
подразумевает inline
, так что одно правило определения все еще применяется. Кроме того, я использую enable_if_condition_enum_t
для исключения на основе SFINAE, если перечисление, используемое в качестве аргумента, не объявлено как std::error_condition
ENUM.
ОБНОВИТЬ
После ответа @Angew вот правильное объявление и определение error_enum_to_int
,
// Declaration
template <typename T, typename = enable_if_condition_enum_t<T>>
constexpr error_id_type error_enum_to_int(T elem) noexcept;
// Definition
template <typename T, typename>
constexpr error_id_type error_enum_to_int(T elem) noexcept {
return static_cast<error_id_type>(elem);
};
1 ответ
В C++ нельзя указывать аргумент по умолчанию или аргумент шаблона по умолчанию более одного раза для одного и того же параметра / параметра шаблона. Аргументы [шаблона] по умолчанию из всех объявлений (включая определение) функции объединяются (каскадируются). Вы должны удалить аргумент шаблона по умолчанию из определения шаблона.