2016-10-18 11 views
12

Szablony decydują o parametryzacji sygnatury funkcji poza samą nazwą funkcji. Ale czy można również sparametryzować stałą funkcji składowej?Czy można sparametryzować stałość funkcji elementu szablonu?

Trivial, minimalistyczny, bez matrycy przykład:

struct Foo { 
    Foo *  self()  { return this; } 
    Foo const * self() const { return this; } 
}; 

vs straw-man matrycy hipotetyczny:

struct Foo { 
    template<typename T> T self() std::constness_of(T) { return this; } 
}; 
+4

Twoje drugie pytanie powinno zostać usunięte. Mogło, ale wątpię, żeby ktokolwiek mógł to udowodnić. – NathanOliver

+0

Nie jest jednak trudno wdrożyć tę funkcjonalność za pomocą specjalistycznego szablonu klasy podstawowej. –

+5

'const (wyrażenie)' byłoby miłe. Wraz z 'noexcept (auto)' – krzaq

Odpowiedz

7

Ale czy można również sparametryzować stałą funkcji członka?

Nie, nie możesz. Nie masz dostępu w sygnaturze funkcji do niejawnego obiektu, na który wskazuje this, więc nie można go w żaden sposób wysyłać na nim ani na szablonie. kwalifikatory cv w funkcjach składowych muszą być określone.

Aby uzyskać bardziej skomplikowane funkcje składowe, można wywołać jedno z nich (zazwyczaj non-const wywołując numer const, aby uniknąć UB), aby uniknąć powielania kodu.


Albo zawsze można napisać niebędącego państwem friend:

struct Foo { 
    template <class T, 
     std::enable_if_t<std::is_base_of<Foo, std::decay_t<T>>::value>* = nullptr 
     > 
    friend T* self(T& x) { return &x; } 
}; 

musimy SFINAE aby self() nie znaleziono dla niespodziewanych typów jak Wrapper<Foo>. Zauważ, że jest to znacznie dłużej niż twój oryginalny kod, więc ma to sens tylko w kontekście skomplikowanej logiki.

Czy na pewno być zabawny czy UFCS został przyjęty i teraz wszyscy napisać nasze const/innych niż const przeciążeń za pośrednictwem będącego członkiem friend ów, że nadal wywoływać jakby byli członkami.

+0

Re "Nie masz dostępu w podpis funkcji do niejawnego obiektu, do którego to wskazuje", jest to technicznie poprawne, ale tylko dlatego, że podpis funkcji nie zawiera typu zwracanego. To znaczy. podczas gdy wniosek dotyczący templowania stałej funkcji członka (tego nie można) jest poprawny, rozumowanie nie jest prawidłowe. –

+0

@ Cheersandhth.-Alf Nie jest to typ zwracany, którego potrzebujesz ... Typ zwrotu jest oparty na kwalifikacjach cv tego "tego". – Barry

+0

W porządku, wniosek jest poprawny. Ale rozumowanie (bit, którego cytowałem) jest błędne dla funkcji * type *: jest dosłownie poprawne, dla funkcji * signature *. A może jestem głupi i chciałeś być bardzo precyzyjny, ale proszę wyjaśnij czytelnikom różnicę między podpisem a typem (nie sądzę, że większość czytelników SO będzie świadoma). –

-1

Nie, ale jest obejście prosto do przodu, a może i więcej czytelny, zamiar jest jasny:

struct Foo { 
    template<typename T> 
    std::enable_if_t<std::is_const<T>::value,T> self() const { return this; } 
    template<typename T> 
    std::enable_if_t<!std::is_const<T>::value,T> self() { return this; } 
}; 
+1

Może to nie było jasne, ale celem jest dostosowanie stałej funkcji do woli bez potrzeby powtarzania kodu, jeśli nie jest on potrzebny. W minimalistycznym przykładzie powtórna implementacja jest banalna, w wielu przypadkach nie jest. – Catskul

+2

Jest to oczywiście mniej czytelne niż kod OP ... a następnie zamiast wywoływania 'x.self()' musiałbyś wywołać 'x.self ()' wszędzie. -1. – Barry

1

W a comment na inną odpowiedź Ci wyjaśnić, że

celem jest dostosowanie constness funkcji do woli, bez konieczności duplikat kodu, gdzie nie jest to inaczej potrzebne

Poniżej jedna możliwość, wyrażająca zarówno wersje funkcji składowej const, jak i elementarną funkcję wzorcową.

Dla bardziej ogólnego przypadku należy przekazać argumenty.

Dwie alternatywy to wyrażanie funkcji składowej const pod względem funkcji składowej innej niż const lub odwrotnie. Ale jak pamiętam, wymaga to nieco brzydkiego rzucania. Albo jakąś brzydotę, nie jestem pewien (przepraszam, teraz siedzę na bardzo ograniczonym łączu internetowym).

#include <string> 

//--------------------------------------- Machinery: 

template< class Guide, class Result > 
struct With_const_like_t_ 
{ 
    using T = Result; 
}; 

template< class Guide, class Result > 
struct With_const_like_t_<Guide const, Result> 
{ 
    using T = Result const; 
}; 

template< class Guide, class Result > 
using With_const_like_ = typename With_const_like_t_<Guide, Result>::T; 


//--------------------------------------- Example usage: 

class Bork 
{ 
private: 
    std::string s_ = "42"; 

    template< class This_class > 
    static auto foo_impl(This_class& o) 
     -> With_const_like_<This_class, std::string>& 
    { return o.s_; } 

public: 
    auto foo() 
     -> decltype(foo_impl(*this)) 
    { return foo_impl(*this); } 

    auto foo() const 
     -> decltype(foo_impl(*this)) 
    { return foo_impl(*this); } 
}; 

#include <iostream> 
#include <typeinfo> 
using namespace std; 

auto main() 
    -> int 
{ 
    Bork v; 
    Bork const c; 
    v.foo() = "Hi there!"; 
    #ifdef TEST 
     c.foo() = "This assignment to `const` won't compile."; 
    #endif 
    cout << v.foo() << endl; 
} 
Powiązane problemy