Разница между g ++ и clang ++ с enable_if - PullRequest
0 голосов
/ 12 марта 2020

Я хочу написать функцию, которая возвращает экземпляр типа T, но ведет себя по-разному в зависимости от того, как можно построить T. Скажем, у меня есть такие структуры

#include <type_traits>                                                                                                                                                                                                                                                                                                        
#include <iostream>

struct A {}; 
struct B {}; 
struct C { 
  C(A a) {
    std::cout << "C" << std::endl;
  }
};

Я хочу создать C, задав им A. У меня есть такая структура, которая использует enable_if для выбора одной из двух функций:

struct E {

  template< bool condition = std::is_constructible<C, A>::value,std::enable_if_t<condition,int> = 0>
  C get() {
    return C{A{}};
  }
  template< bool condition = std::is_constructible<C, B>::value,std::enable_if_t<condition,bool> = false>
  C get() {
    return C{B{}};
  }
};

Это прекрасно компилируется с g ++ 82 (и я думаю также с g ++ 9), но clang9 выдает мне ошибку

$ clang++ --std=c++17 main.cpp 
main.cpp:26:12: error: no matching constructor for initialization of 'C'
    return C{B{}};
           ^~~~~~
main.cpp:6:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'B' to 'const C' for 1st argument
struct C {
       ^
main.cpp:6:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'B' to 'C' for 1st argument
struct C {
       ^
main.cpp:7:3: note: candidate constructor not viable: no known conversion from 'B' to 'A' for 1st argument
  C(A a) {
  ^
1 error generated.

, хотя enable_if должен скрывать эту функцию. (Звоню E e; auto c = e.get();). Если я не жестко кодирую C, а вместо этого использую шаблон для передачи C, он работает в обоих компиляторах.

template<typename T>
struct F {

  template< bool condition = std::is_constructible<T, A>::value,std::enable_if_t<condition,int> = 0>
  T get() {
    return T{A{}};
  }
  template< bool condition = std::is_constructible<T, B>::value,std::enable_if_t<condition,bool> = false>
  T get() {
    return T{B{}};
  }
};

Я не понимаю, почему clang явно проверяет тело функции даже хотя функция должна быть отключена enable_if.

Ответы [ 2 ]

2 голосов
/ 12 марта 2020

Оба компилятора правы,

http://eel.is/c++draft/temp.res#8 .1

Достоверность шаблона может быть проверена перед любой реализацией. [Примечание: Зная, какие имена являются именами типов, можно таким образом проверять синтаксис каждого шаблона. - примечание к концу] Программа некорректна, диагностика не требуется c, если:

(8.1)
- для шаблона или подстановки оператора constexpr if не может быть сформирована действительная специализация в шаблоне, а шаблон не создан, или

[..]

(8.4)
- гипотетическое создание шаблона сразу после его определения будет некорректным из-за для конструкции, которая не зависит от параметра шаблона, или

return C{B{}}; не зависит от шаблона и неверно. clang в порядке, диагностируя проблему.

2 голосов
/ 12 марта 2020

Поскольку у вас, похоже, есть доступ к компиляторам, поддерживающим c ++ 17, вы можете использовать if constexpr вместо enable_if для достижения sh того, что вы хотите.

#include <iostream>
#include <type_traits>

struct A {};

struct B {};

struct C {
    explicit C(A a) {
        std::cout << "C" << std::endl;
    }
};

template<typename T>
struct False : std::false_type {};

struct E {
    template<typename T = void>
    C get() const {
        if constexpr (std::is_constructible_v<C, A>) {
            return C{A{}};
        } else if constexpr (std::is_constructible_v<C, B>) {
            return C{B{}};
        } else {
            static_assert(False<T>::value, "Error");
        }
    }
};

int main() {
    const auto C{E{}.get()};
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...