2013-10-04 11 views
5

Mam funkcji z tymi przeciążeniami:Zwracanie wartości z tego samego constness jako parametr

A& f(B& b) 
{ 
    return b.a; 
} 

const A& f(const B& b) 
{ 
    return b.a; 
} 

Instrukcja return może być znacznie bardziej skomplikowana logika, która zwraca pewną wartość, która jest const jeśli b jest const. Na przykład: B to kontener kontenerów (prawdopodobnie z jeszcze głębszym zagnieżdżeniem) i szukamy jednego elementu. W takim przypadku skopiowanie funkcji nie jest dobrym pomysłem. Czy są jakieś alternatywy dla osiągnięcia tego samego rezultatu?

Potrafię wymyślić jakieś rozwiązania, ale tak naprawdę ich nie lubię.

template <typename T> 
auto f(T& b) -> decltype(b.a) 
{ 
    return b.a; 
} 

Działa jednak komplikuje się jeśli b.a nie jest tak trywialne (jak w zw pojemników).

A& f(B& b) 
{ 
    return b.a; 
} 

const A& f(const B& b) 
{ 
    return f(const_cast<B&>(b)); 
} 

Działa to również, ale jeśli jest to hak. Czy istnieje proste i czyste rozwiązanie?

+1

Co z 'A & f (B & b) {return f (const_cast (n));}'?Dodanie 'const' nie jest hackerem, a wciąż masz wszystkie gwarancje. Zakładam, że funkcja może zawsze działać na wejściu const, co wydaje się sugerować pytanie. – Jon

+0

Następnie musiałbym const_cast the output, więc jest w zasadzie taki sam. Ale tak, może to jest bezpieczniejsze. – petersohn

Odpowiedz

3

Wdrażałbym wersję niestałą pod względem wersji const, a nie odwrotnie (tak jak to robiłeś).

A& f(B& b) 
{ 
    return const_cast<A&>(f(static_cast<B const&>(b))); 
} 

Wygląda nieco bezpieczniejsze z punktu widzenia zmienności.

Co do wersji szablonu, jak ten temat:

template <typename T> 
auto f(T& b) -> std::conditional_t<std::is_const<T>{}, A const&, A&> 
{ 
    return b.a; 
} 

Jeśli std::conditional_t (C++ 14) nie jest obsługiwany, a następnie użyć std::conditional:

typename std::conditional<std::is_const<T>{}, A const&, A&>::type 

nadzieję, że pomoże.

+1

Hm, myślę, że masz rację, wersja non-const powinna być zaimplementowana w kategoriach const ... Zawsze się mylę co do tego, która droga jest właściwa. –

+0

Nie powinno 'is_const {}' dla 'std :: conditional' to' std :: is_const :: value'? – 0x499602D2

+0

@ 0x499602D2: Ma niejawną funkcję konwersji: 'constexpr operator bool() const'. To zostanie wywołane niejawnie i automatycznie otrzyma "wartość". – Nawaz

2

Jeśli wiesz, że f nie zmutowuje danych, to const_cast nie jest hackerem, ale jest całkowicie uzasadnionym rozwiązaniem.

Typowym przykładem tego jest strchr, który znajduje znak w ciągu znaków. Mamy znać, że to nie mutuje, ale jest całkowicie rozsądne, aby umożliwić mutable string zostać zmutowany, oferując nie const const. Jednak wersja niestacjonarna może być z powodzeniem zaimplementowana pod względem przeciążenia const.

Pamiętaj, że korzystanie z const_cast nie jest pogwałceniem. Jest to tylko naruszenie mutacji stałego obiektu obiektu.

2

Możliwym rozwiązaniem:

template<typename B> 
struct Return_type { typedef A &Type; }; 

template<typename B> 
struct Return_type<B const &> { typedef A const &Type; }; 

template<typename B> 
typename Return_type<B>::Type f(B &&b) 
{ 
    return b.a; 
} 

EDIT:

C++ 14 będzie mieć typ zwracany funkcja odliczania, więc to powinno działać:

template<typename T> 
auto &f(T &&b) 
{ 
    return b.a; 
} 

gcc 4.8.1 już kompiluje to.

+0

Nowy typ typedef 'using Type = A const &;' wygląda znacznie lepiej. – Nawaz

+0

Czy "auto" nie odrzuca const-ness? – Hulk

+0

+1 dla rozwiązania C++ 14. Myślałem o tym również, ale rozważałem tylko rozwiązania poprawne w C++ 11. – petersohn

Powiązane problemy