Лямбда-выражение как недооцененные подвыражения константного выражения

#c #c 11 #constexpr

#c #c 11 #constexpr

Вопрос:

Извините за длинный пост, но я не могу заставить эту программу работать, если я не укажу -fpermissive в gcc, а вовсе не в clang. Не могли бы вы, пожалуйста, помочь исправить этот пример?

 namespace detail
{

template<typename T>
constexpr auto address(Tamp;amp; t) ->
  typename ::std::remove_reference<T>::type*
{
  return amp;t;
}

template <typename FP, FP fp, class C, typename ...A>
struct S
{
  static constexpr auto* l = false ? address(
    [](C* const object) noexcept
    {
      return [object](Aamp;amp; ...args) {
        return (object->*fp)(::std::forward<A>(args)...); 
      };
    }) :
    nullptr
  ;
};

template <typename FP, FP fp, typename R, class C, typename ...A>
auto make_member_delegate(C* const object, R (C::* const)(A...)) ->
  decltype((*S<FP, fp, C, A...>::l)(object))
{
  return (*S<FP, fp, C, A...>::l)(object);
}

}

template <typename FP, FP fp, class C>
auto make_member_delegate(C* const object) ->
  decltype(detail::make_member_delegate<FP, fp>(object, fp))
{
  return detail::make_member_delegate<FP, fp>(object, fp);
}

struct A
{
  void hello()
  {
    ::std::cout << "it worked" << ::std::endl;
  }
};

int main()
{
  A a;

  auto d(make_member_delegate<decltype(amp;A::hello), amp;A::hello>(amp;a));

  d();

  return 0;
}
  

Ошибки (сначала gcc, затем clang ):

gcc-4.9.0:

 t.cpp:20:26: error: 'constexpr detail::S<void (A::*)(), amp;A::hello, A>::<lambda(A*)>* const detail::S<void (A::*)(), amp;A::hello, A>::l', declared using local type 'detail::S<void (A::*)(), amp;A::hello, A>::<lambda(A*)>', is used but never defined [-fpermissive]
   static constexpr auto* l = false ? address(
  

clang -3.4.2:

 t.cpp:21:5: error: a lambda expression may not appear inside of a constant expression
    [](C* const object) noexcept
    ^
t.cpp:33:14: note: in instantiation of template class 'detail::S<void (A::*)(), amp;A::hello, A>' requested here
  decltype((*S<FP, fp, C, A...>::l)(object))
             ^
t.cpp:32:6: note: while substituting deduced template arguments into function template 'make_member_delegate' [with FP = void
      (A::*)(), fp = amp;A::hello, R = void, C = A, A = <>]
auto make_member_delegate(C* const object, R (C::* const)(A...)) ->
     ^
t.cpp:41:6: note: while substituting deduced template arguments into function template 'make_member_delegate' [with FP = void
      (A::*)(), fp = amp;A::hello, C = A]
auto make_member_delegate(C* const object) ->
  

Как ни странно, канонический пример PYTHY компилируется без проблем в clang-3.4.2

Комментарии:

1. clang правильный, у вас не может быть лямбда-выражения в постоянном выражении. Есть ли какая-либо причина, по которой вы считаете, что этот код следует компилировать?

2. @Brian Посмотри сюда и попробуй скомпилировать с clang, и это сработает.

3. Быстрое и грязное исправление

4. @Solkar Возможно, это относится к категории единообразной инициализации (с использованием связанного списка инициализации в инструкции return или для создания параметра функции). В C 1y мы, вероятно, будем видеть это реже в пользу вывода возвращаемого типа.

5. @dyp: Да, код PYTH формально вызывает UB. Автор PYTHY пытается объяснить это как «ок», потому что он ни к чему не обращается внутри лямбда-выражения — UB все еще остается UB.

Ответ №1:

Вот исправление для gcc-4.9.0 , но программа по-прежнему не будет компилироваться с clang-3.4.2 :

 template <typename FP, FP fp, typename R, class C, typename ...A>
auto make_delegate(C* const object, R (C::* const)(A...)) ->
  decltype((*decltype(S<FP, fp, C, A...>::l)(nullptr))(object))
{
  return (*decltype(S<FP, fp, C, A...>::l)(nullptr))(object);
}