2017-01-23 12 views
6

Powiedzmy masz te dwie klasy:Wybierz funkcję szablonu na podstawie istnienia członka

class A 
{ 
public: 
    int a; 
    int b; 
} 

class B 
{ 
public: 
    int a; 
    int b; 
} 

class C 
{ 
public: 
    float a1; 
    float b1; 
} 

enum class Side { A, B }; 

chcę funkcję szablonu, który pobiera side i T, oraz w zależności od T, zwraca odwołanie do " T.a "lub" T.b ", jeśli klasa ma członka T::a, lub odniesienie do" T.a1 "lub" T.b1 ", jeśli klasa ma członka T::a1.

Moim punktem wyjścia jest:

template<typename T> 
auto &GetBySide(const Side &side, const T &twoSided) 
{ 
    return side == Side::A?twoSided.a:twoSided.b; 
} 

template<typename T> 
auto &GetBySide(const Side &side, const T &twoSided) 
{ 
    return side == Side::A?twoSided.a1:twoSided.b1; 
} 

Chodzi o to, jak dostać kompilator pominąć pierwszy szablon jeśli członek a nie istnieje.

Więc zaimplementowałem rozwiązanie podane przez @ Jarod42 poniżej, ale dało błędy w VS 2015 z powodu błędu w umiejętności VS do rozróżniania szablonów. Tutaj znajduje się obejść:

template<typename T> 
auto GetBySide(const Side &side, const T& twoSided) 
-> decltype((twoSided.a)) 
{ 
    return side == Side::A ? twoSided.a : twoSided.b; 
} 

// Using comma operator to trick compiler so it doesn't think that this is the same as above 
template<typename T> 
auto GetBySide(const Side &side, const T &twoSided) 
-> decltype((0, twoSided.a1)) 
{ 
    return side == Side::A ? twoSided.a1 : twoSided.b1; 
} 

// See comment above 
template<typename T> 
auto GetBySide(const Side &side, const T &twoSided) 
-> decltype((0, 0, twoSided.a2)) 
{ 
    return side == Side::A ? twoSided.a2 : twoSided.b2; 
} 

Innym sposobem byłoby użyć operatora przecinek oraz specjalny struct których każdy reprezentowany „pojęcie”

Odpowiedz

11

Z SFINAE.

template<typename T> 
auto GetBySide(const Side &side, const T& twoSided) 
-> decltype((twoSided.a)) 
{ 
    return side == Side::A ? twoSided.a : twoSided.b; 
} 

template<typename T> 
auto GetBySide(const Side &side, const T &twoSided) 
-> decltype((twoSided.a1)) 
{ 
    return side == Side::A ? twoSided.a1 : twoSided.b1; 
} 

Demo

+0

To jeden ze sposobów, które próbowałem, ale ja już otrzymuję błąd z Visual Studio stwierdzające, że szablon funkcja została już zdefiniowana. – bpeikes

+0

@ Jarod42 Dzień, w którym zdajesz sobie sprawę, że 'auto f() -> decltype ((wyrażenie)) zwraca referencję (i mówisz - że to oczywiste, jak to się stało, że nigdy wcześniej o tym nie myślałem?) . Dobry połów. +1 – skypjack

+0

@skypjack - w rzeczywistości nie zwraca referencji. Upadłem w tę dziurę. Aby zwrócić referencję, potrzebujesz: auto f() -> decltype (wyrażenie) i – bpeikes

Powiązane problemy