Подтвердить что ты не робот

Объявление функции-члена с использованием синтаксиса типа функции

Совсем недавно я узнал, что вы можете объявить функцию (включая методы), используя синтаксис типа типа с типом функции:

using function_type = int (double);
// pre-C++11:
//typedef int function_type(double);

function_type fun_global;

struct methods
{
    static function_type mem_fun_static;
    function_type mem_fun_normal;
    virtual function_type mem_fun_virtual;
    virtual function_type mem_fun_abstract = 0;
};

В приведенном выше коде

  • fun_global - глобальная функция,
  • mem_fun_static - это функция-член static,
  • mem_fun_normal - обычный метод,
  • mem_fun_virtual - это метод virtual,
  • mem_fun_abstract - абстрактный метод.

Все они принимают один аргумент типа double и возвращают значение int - точно так же, как говорит function_type.

Все эти годы я знаю С++, и я не знал об этом - этот язык никогда не перестает удивлять меня! Кстати, этот синтаксис упоминается где-то здесь? Я не вижу этого...

Однако, исследуя эту новую функцию, я наткнулся на некоторые несоответствия между компиляторами. Для тестов я использовал следующие компиляторы:

  • GCC 5.4.0 и 7.1.0, командная строка: g++ -Wall -Wextra -pedantic -std=c++14
  • Clang 4.0.1, командная строка: clang++ -Wall -Wextra -pedantic -std=c++14
  • MSVC 19.10.25019 (VS 2017), командная строка: cl /W4 /EHsc

В тестах, которые я запускаю обе версии GCC, вы получили такой же результат, поэтому я имею в виду их как GCC.


= delete несогласованность

struct methods
{
    /* ... */
    function_type mem_fun_deleted = delete;
};
  • GCC: OK
  • Clang: ошибка!

    Test.cpp:13:34: error: '= delete' is a function definition and must occur in a standalone declaration
            function_type mem_fun_deleted = delete;
                                            ^
    1 error generated.
    
  • MSVC: OK

= default несогласованность

struct methods
{
    /* ... */
    using assignment_type = methods& (methods const&);
    assignment_type operator= = default;
};
  • GCC: OK
  • Clang: ошибка!

    Test.cpp:14:30: error: '= default' is a function definition and must occur in a standalone declaration
            assignment_type operator= = default;
                                        ^
    1 error generated.
    
  • MSVC: ошибка!

    Test.cpp(14): error C2206: 'methods::operator =': typedef cannot be used for function definition
    

Несоответствие определения строки

struct methods
{
    /* ... */
    function_type mem_fun_inline { return 0; }
};
  • GCC: ошибка!

    Test.cpp:13:43: error: invalid initializer for member function ‘int methods::mem_fun_inline(double)’
      function_type mem_fun_inline { return 0; }
                                               ^
    Test.cpp:13:43: error: expected ‘;’ at end of member declaration
    
  • Clang: ошибка!

    Test.cpp:13:33: error: expected expression
            function_type mem_fun_inline { return 0; }
                                           ^
    Test.cpp:7:8: error: missing '}' at end of definition of 'methods'
    struct methods
           ^
    /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/x86_64-pc-cygwin/bits/c++config.h:194:1: note: still within definition of 'methods' here
    namespace std
    ^
    2 errors generated.
    
  • MSVC: OK


Вопросы

Какие компиляторы здесь?

Кроме того, возможно ли:

  • В встроенном определении (поддерживается только MSVC) как-то ссылаются на аргумент?
  • Как-то использовать function_type также при определении этих функций (когда это делается вне класса). Следующее - ОК (со всеми компиляторами)

    struct methods
    {
        static function_type mem_fun_static;
        /* ... */
    };
    
    int methods::mem_fun_static(double) { return 0; }
    

    Это не так уж плохо, так как изменение function_type должно приводить к ошибке компиляции при определении функции (так как она больше не будет соответствовать объявлению) - но все же возможно избежать этого даже.

4b9b3361

Ответ 1

§ 8.3.5 Функции [dcl.fct] p12 Для объявления функции может использоваться typedef типа функции, но не используется для определения функции.

Таким образом, Clang прав отклонить код во всех случаях, а компиляторы, которые его принимают, ошибаются.

(Цитата из N4618, но это правило является частью языка, поскольку навсегда).