Std :: result_of ve decltype arasındaki fark


100

std::result_ofC ++ 0x'in ihtiyacını anlamakta biraz sorun yaşıyorum . Doğru anladıysam, result_ofbelirli türde parametrelerle bir işlev nesnesini çağırmanın sonuç türünü elde etmek için kullanılır. Örneğin:

template <typename F, typename Arg>
typename std::result_of<F(Arg)>::type
invoke(F f, Arg a)
{
    return f(a);
}

Aşağıdaki kodla farkı gerçekten görmüyorum:

template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(f(a)) //uses the f parameter
{
    return f(a);
}

veya

template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(F()(a)); //"constructs" an F
{
    return f(a);
}

Bu iki çözümde görebildiğim tek sorun şudur:

  • decltype'a iletilen ifadede kullanmak için bir functor örneğine sahip olun.
  • functor için tanımlı bir kurucu bilir.

Ben arasındaki tek fark bu doğru düşünme mıyım decltypeve result_ofikinci bunu yapmaz İlki bir ifade ihtiyacı var?

Yanıtlar:


86

result_ofBoost'ta tanıtıldı ve ardından TR1'e ve son olarak C ++ 0x'e dahil edildi. Bu nedenle result_ofgeriye dönük uyumlu (uygun bir kitaplıkla) bir avantajı vardır.

decltype C ++ 0x'de tamamen yeni bir şeydir, yalnızca bir işlevin türünü döndürmekle sınırlandırmaz ve bir dil özelliğidir.


Her neyse, gcc 4.5'te result_ofşu şekilde uygulanır decltype:

  template<typename _Signature>
    class result_of;

  template<typename _Functor, typename... _ArgTypes>
    struct result_of<_Functor(_ArgTypes...)>
    {
      typedef
        decltype( std::declval<_Functor>()(std::declval<_ArgTypes>()...) )
        type;
    };

4
Anladığım kadarıyla decltypedaha çirkin ama aynı zamanda daha güçlü. result_ofyalnızca çağrılabilir türler için kullanılabilir ve bağımsız değişken olarak türler gerektirir. Örneğin, result_ofburada kullanamazsınız : template <typename T, typename U> auto sum( T t, U u ) -> decltype( t + u );eğer argümanlar aritmetik türler olabilirse ( temsil etmek Fiçin tanımlayabileceğiniz bir işlev yoktur . Kullanıcı tanımlı türler için yapabilirsiniz. Aynı şekilde (onunla gerçekten oynamadım) üye yöntemlerine çağrı yapmak bağlayıcı veya lambdas kullanmadan yapmak zor olabilirF(T,U)t+uresult_of
David Rodríguez - dribeas

2
Bir not, decltype işlevini AFAIK ile çağırmak için bağımsız değişkenlere ihtiyaç duyar, bu nedenle sonuç_of <> olmadan, geçerli varsayılan oluşturuculara sahip bağımsız değişkenlere güvenmeden bir şablon tarafından döndürülen türü elde etmek gariptir.
Robert Mason

3
@RobertMason: Bu argümanlar, std::declvalyukarıda gösterdiğim kod gibi kullanılarak alınabilir . Tabii ki, bu çirkin :)
kennytm

1
@ DavidRod Rodríguez-dribeas Yorumunuz "(var" :(
Navin

1
Başka not: result_ofve yardımcı tip result_of_tC ++ lehine 17 olarak önerilmemektedir invoke_resultve invoke_result_teski bazı kısıtlamalar hafifletmiştir. Bunlar en.cppreference.com/w/cpp/types/result_of'un altında listelenmiştir .
sigma

11

İşlev çağrısı gibi olmayan bir şeye ihtiyacınız varsa std::result_of, bu geçerli değildir. decltype()size herhangi bir ifadenin türünü verebilir.

Kendimizi bir işlev çağrısının dönüş türünü ( std::result_of_t<F(Args...)>ve arasında decltype(std::declval<F>()(std::declval<Args>()...)) belirlemenin farklı yollarıyla sınırlarsak , o zaman bir fark vardır.

std::result_of<F(Args...) olarak tanımlanır:

İfade INVOKE (declval<Fn>(), declval<ArgTypes>()...), değerlendirilmemiş bir işlenen olarak ele alındığında (Madde 5) iyi biçimlendirilmişse, üye typedef türü, türü decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...)); başka şekilde adlandırır, üye türü olmaz.

Arasındaki fark result_of<F(Args..)>::typeve decltype(std::declval<F>()(std::declval<Args>()...)hepsi hakkında INVOKE. Yazmak için biraz daha uzun olmasına ek olarak declval/ decltypedoğrudan kullanmak , yalnızca Fdoğrudan çağrılabilirse geçerlidir (bir işlev nesnesi türü veya bir işlev veya bir işlev işaretçisi). result_ofayrıca üye işlevlerine işaretçileri ve üye verilerine işaretçileri destekler.

Başlangıçta, SFINAE dostu bir ifadeyi kullanmak declval/ decltypegarantilemek, std::result_ofkesinti hatası yerine size sert bir hata verebilir. Bu C ++ 14'te düzeltildi: std::result_ofartık SFINAE dostu olması gerekiyor ( bu makale sayesinde ).

Dolayısıyla uyumlu bir C ++ 14 derleyicisinde, std::result_of_t<F(Args...)>kesinlikle üstündür. Daha net, daha kısa ve doğru bir şekilde daha fazla Fs destekliyor .


† Diğer bir deyişle, üyelere işaretçilerin girmesine izin vermek istemediğiniz bir bağlamda kullanmıyorsanız std::result_of_t, başarısız olmasını isteyebileceğiniz bir durumda başarılı olursunuz.

İstisnalar dışında. Üyelere işaretçileri desteklerken, result_ofgeçersiz bir tür kimliği başlatmaya çalışırsanız çalışmayacaktır . Bunlar, bir işlevi döndüren veya değere göre soyut türleri alan bir işlevi içerir. Ör .:

template <class F, class R = result_of_t<F()>>
R call(F& f) { return f(); }

int answer() { return 42; }

call(answer); // nope

Doğru kullanım olurdu result_of_t<F&()>, ama bu hatırlamanız gerekmeyen bir ayrıntı decltype.


Referans olmayan bir tür Tve bir işlev template<class F> result_of_t<F&&(T&&)> call(F&& f, T&& arg) { return std::forward<F>(f)(std::move(arg)); }için kullanımı result_of_tdoğru mu?
Zizheng Tai

Ayrıca, ilettiğimiz argüman fa ise const T, kullanmalı mıyız result_of_t<F&&(const T&)>?
Zizheng Tai
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.