2016-02-23 3 views
3

У меня есть класс, который в основном управляет только вектором пользовательских типов. Для того, чтобы избавить меня от написания той же итерации цикла снова и снова я написал следующую конструкцию:Как вывести возвращаемый тип объекта std :: bind для использования шаблона?

template<typename T> 
uint64_t ACCUMULATE_ON_VECTOR(const std::vector<MyClass*> vec, T method) 
{ 
    return std::accumulate(vec.begin(), vec.end(), 0, [&](uint64_t acc, const MyClass* c) 
    { 
    return acc + (c ? method(c) : 0); 
    }); 
} 

которая затем вызывается так:

ACCUMULATE_ON_VECTOR(_myVec, std::bind(&MyClass::someMethod, std::placeholders::_1)); 

Это работает очень хорошо в концепции, но так как у меня много методов, возвращающих разные целые типы (signed/unsigned, signed/unsigned long), я бы хотел отвлечься от hardcoded uint64_t, потому что я получаю предупреждения компилятора повсюду. Для этого мне как-то нужно получить возвращаемый тип объекта bind. Могу ли я как-то это сделать с помощью decltype? То, что я ищу это:

template<typename T> 
<new deduced type> ACCUMULATE_ON_VECTOR(const std::vector<MyClass*> vec, T method) 
{ 
    return std::accumulate(vec.begin(), vec.end(), 0, [&](<new deduced type> acc, const MyClass* c) 
    { 
    return acc + (c ? method(c) : 0); 
    }); 
} 
+1

ли это необходимость работать с C++ 11, или C++ 14 нормально? Если это так, вы можете использовать 'auto'. – Michael

+0

Может ли ['result_of'] (http://en.cppreference.com/w/cpp/types/result_of) вам помочь? –

ответ

4

Вы можете сделать это с помощью std::result_of:

template<typename Func> 
typename std::result_of<Func(MyClass*)>::type 
ACCUMULATE_ON_VECTOR(const std::vector<MyClass*> &vec, Func method) 
{ 
    using ResultType = typename std::result_of<Func(MyClass*)>::type; 
    return std::accumulate(vec.begin(), vec.end(), ResultType{}, 
         [&](typename std::result_of<Func(MyClass*)>::type acc, 
          const MyClass* c) 
    { 
    return acc + (c ? method(c) : ResultType{}); 
    }); 
} 

Примечание Я значение инициализации типа возвращаемого вместо того, чтобы использовать целочисленный литерал нуль.

Может быть более читаемым, чтобы обернуть параметр функции в std::function<ResultT(MyClass*)>: функция накапливания будет непосредственно заархивирована по типу результата, подталкивая ответственность за это до места вызова.


Кстати, вам не нужно/ведомой техника типа auto возвращения сюда, потому что тип возвращаемого значения не зависит от списка аргументов, только от параметра шаблона - тем не менее, я думаю, это выглядит немного лучше :

template<typename Func> 
auto ACCUMULATE_ON_VECTOR(const std::vector<MyClass*> &vec, 
          Func method) 
-> typename std::result_of<Func(MyClass*)>::type 
{ 
0

При использовании C++ 14 это нормально, вы можете просто заменить ваш тип тайны для auto.

Если вы ограничены C++ 11 вы можете использовать следующие несколько уродливое решение:

template<typename T> 
auto ACCUMULATE_ON_VECTOR(const std::vector<MyClass*> vec, T method) -> 
    typename std::result_of<decltype(method)(MyClass*)>::type 
{ 
    return std::accumulate(vec.begin(), vec.end(), 0, 
    [&](typename std::result_of<decltype(method)(MyClass*)>::type acc, const MyClass* c) 
    { 
    return acc + (c ? method(c) : 0); 
    }); 
} 

Это дает тип, результат вызова method на MyClass* будет иметь.


Кстати, там на самом деле нет необходимости использовать bind на месте вызова. Вы можете достичь того же с помощью лямбда:

ACCUMULATE_ON_VECTOR(_myVec, [] (const MyClass* p) { return p->someMethod(); }); 

(В C++ 14 можно заменить const MyClass* с auto, если вы хотите)

3

std::bind имеет тип элемента под названием result_type, который

1) Если F является указателем на функцию или указателем на функцию-член, result_type является возвращаемым типом F. Если F - тип класса с вложенным typedef result_type, то result_type - F :: result_type. В противном случае не будет определен параметр result_type.

2) result_type именно Р.

Мы можем использовать косую тип возвращаемого значения, как

template<typename T> 
auto ACCUMULATE_ON_VECTOR(const std::vector<MyClass*> vec, T method) -> typename T::result_type 
{ 
    typename T::result_type begin = 0; 
    return std::accumulate(vec.begin(), vec.end(), begin, [&](typename T::result_type acc, const MyClass* c) 
    { 
    return acc + (c ? method(c) : 0); 
    }); 
} 

это будет работать с C++ 11 и выше.

Я также добавил typename T::result_type begin = 0; к функции, поэтому тип, переданный для накопления, является тем же самым типом, что и возврат функции. Поскольку у вас только что был 0, который выводится на int, а accumulate всегда будет возвращать int.

+1

Этот 'result_type' не очень полезен в общем коде. –

+0

@ T.C. Я согласен, но я хотел, чтобы OP знал об этом и как его можно было использовать. Я фактически проголосовал за принятый ответ, поскольку я чувствую, что это лучшая работа. – NathanOliver

0

Тип результата накопления не имеет ничего общего с переданной функцией накапливания. Тип результата накопления - это тип третьего параметра. Вы получаете предупреждения, потому что ваш третий параметр: 0 (который является int), так что int64 будет усечен до int в любом случае!Вы должны передать int46(0) в качестве третьего параметра для кода без предупреждения.

0

Вы можете использовать std::iterator_traits

template<typename T> 
auto ACCUMULATE_ON_VECTOR(const std::vector<MyClass*> vec, T method) 
    -> typename std::iterator_traits<T(MyClass*)>::value_type 
{ 
    return std::accumulate(
     vec.begin(), 
     vec.end(), 
     0, 
     [&](typename std::iterator_traits<T(MyClass*)>::value_type acc, const MyClass* c) 
     { 
      return acc + (c ? method(c) : 0); 
     } 
    ); 
} 
+0

Я не думаю, что это то, что 'std :: iterator_traits' для –

Смежные вопросы