Вызов функции-члена в decltype

Следующий код:

struct A
{
    int f(int);
    auto g(int x) -> decltype(f(x));
};

Не компилируется с ошибкой:

error: cannot call member function 'int B::f(int)' without object

Если я изменю его на:

struct A
{
    int f(int);
    auto g(int x) -> decltype(this->f(x));
};

Я получаю другую ошибку:

error: invalid use of 'this' at top level

Что не так с любым из них? Я использую gcc 4.6


person HighCommander4    schedule 28.02.2011    source источник
comment
Вы пробовали decltype( declval<A>().f() )? Я думаю, что так работает С++ 0x result_of. См. stackoverflow.com/questions /2689709/   -  person KitsuneYMG    schedule 01.03.2011
comment
@Kitsune, ни decltype(declval<A>().f()), ни decltype(((A*)0)->f()) не сработают, пробовали. Смотрите мой ответ для чего-то, что работает, но это уродливо.   -  person Mikael Persson    schedule 01.03.2011


Ответы (6)


В настоящее время вы можете получить доступ только к 'this' и членам класса внутри тела функции, но это, вероятно, скоро изменится:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1207

person Bo Persson    schedule 01.03.2011
comment
@YSC - верно, он перемещен в список исправленных дефектов (и был добавлен в стандарт C++11). - person Bo Persson; 31.01.2017

Вот волшебные слова:

struct A
{
    int f(int);
    auto g(int x) -> decltype((((A*)0) ->* &A::f)(x)) ;
};

Изменить Из ответа Микаэля Перссона я вижу, что именно так это делается в boost.

person TonyK    schedule 28.02.2011

Сочетание result_of и decltype может дать тип возвращаемого значения для функции-члена.

#include <type_traits>
using namespace std;

struct A
{
    int f(int i) { return i; } 
    auto g(int x) -> std::result_of<decltype(&A::f)(A, int)>::type
    { 
        return x;
    }
};


int main() {
    A a;
static_assert(std::is_same<decltype(a.f(123)), 
                  decltype(a.g(123))>::value, 
                  "should be identical");
return 0;
}
person Kjell Hedström    schedule 30.04.2014
comment
Я разместил запись в блоге, где можно увидеть различные варианты использования, когда это полезно. - person Kjell Hedström; 01.05.2014

Comeau не любит auto в качестве возвращаемого типа верхнего уровня, но следующее успешно компилируется:

template <typename R, typename C, typename A1> R get_return_type(R (C::*)(A1));

struct A
{
    int f(int);
    decltype(get_return_type(&A::f)) g(int x);
};

По сути, вы должны объявить по крайней мере одну дополнительную конструкцию, которая даст вам нужный тип. И используйте decltype напрямую.

РЕДАКТИРОВАТЬ: Кстати, это отлично подходит для погружения в возвращаемый тип функции-члена:

template <typename R, typename C, typename A1> R get_return_type(R (C::*)(A1));

struct B { int f(int); };

struct A
{
    int f(int);
    B h(int);

    decltype(get_return_type(&A::f)) g(int x);

    decltype(get_return_type(&A::h).f(0)) k(int x);
};

int main()
{
    return A().k(0);
}

Конечно, он не так удобен, как auto f()-> ..., но, по крайней мере, компилируется.

person MSN    schedule 28.02.2011
comment
Кроме того, это не работает, если f является функцией-членом шаблона, если вы явно не выберете правильную перегрузку, что делает ее еще более подробной. - person HighCommander4; 01.03.2011
comment
@ HighCommander4 Вы спрашиваете, есть ли менее уродливый способ сделать это? Я думаю, что нет, по тем же причинам, которые вы привели. - person MSN; 01.03.2011

После некоторых тестов ни decltype(declval<A>().f(x)), ни decltype(((A*)0)->f(x)) работать не будут.

Однако кажется, что использование boost::bind будет работать (и это версия «под капотом»):

struct A
{
    int f(int);
    auto g(int x) -> decltype(boost::bind(&A::f,0,x)());
    auto h(int x) -> decltype((((A*)0)->*(&A::f))(x)); //similarly (what Boost.Bind does under-the-hood.
};

Конечно, это некрасиво. Я думаю, вы можете посмотреть, как это делает boost::bind, чтобы найти более приятное решение.

ИЗМЕНИТЬ

Как предложил MSN, вы также можете создать свой собственный шаблон функции, чтобы решить эту проблему:

template< typename R, typename C, typename... Args > R member_func(R (C::*)(Args...)); 

struct A
{
    int f(int);
    auto g(int x) -> decltype(member_func(&A::f));
};
person Mikael Persson    schedule 28.02.2011
comment
Это не работает. Я получаю: error: invalid use of incomplete type 'A'. Даже если это сработает, это будет довольно многословно, особенно если в возвращаемом выражении есть несколько вызовов функций-членов. - person HighCommander4; 01.03.2011
comment
Хороший вопрос, A является неполным. Вместо этого попробуйте использовать указатель. Смотрите мою правку. - person Mikael Persson; 01.03.2011
comment
Версия указателя также дает error: invalid use of incomplete type 'A' - person HighCommander4; 01.03.2011

Мне кажется, что это не работает, потому что decltype находится вне метода, а A в этот момент является неполным типом (поэтому вы даже не можете сделать A().f(x)).

Но вы не должны действительно нуждаться в этом. Вне объявления A это будет работать, как и ожидалось, в A вы должны знать тип возвращаемого значения функции, которую вы объявили несколькими строками выше. Или вы могли бы просто написать:

struct A {
    typedef int ret_type;
    ret_type f(int x);
    ret_type g(int x);
};

Это работает даже с простым С++ 03.

person Fozi    schedule 28.02.2011
comment
Пример, который я привел, явно упрощен, чтобы подчеркнуть проблему. В моем реальном коде тип возвращаемого значения f — не int, а какое-то сложное выражение, включающее преобразования Boost.Range, выражения связывания и т. д. (на самом деле, f сама использует decltype для объявления своего возвращаемого типа). - person HighCommander4; 01.03.2011
comment
@HighCommander: Значит, вам удалось объявить возвращаемое значение один раз, но не во второй раз? - person UncleBens; 01.03.2011
comment
@UncleBens: Нет, ни разу. Как я уже сказал, f сама использует decltype для объявления возвращаемого типа. - person HighCommander4; 01.03.2011
comment
Вы пробовали typedef decltype(...) ret_type;? - person Fozi; 01.03.2011
comment
Да, у него такая же проблема. - person HighCommander4; 01.03.2011
comment
Но в (...) вы должны указать то, что вы указали в f, чтобы объявить возвращаемый тип, а не то, что вы написали в вопросе. - person Fozi; 03.03.2011