2013-08-16 32 views
11

Sau khi đọc câu trả lời của Matthieu here, tôi quyết định tự mình thử.SFINAE decltype comma operator lừa

Nỗ lực của tôi không biên dịch được vì SFINAE không khởi động và hủy bỏ chức năng has_foo cố gắng truy cập T::foo.

error: ‘struct Bar’ has no member named ‘foo’ 

Tôi có thiếu thứ gì đó hoặc những gì tôi đang cố gắng không thể thực hiện theo cách này?

(Tôi đang sử dụng gcc-4.7.2)

Full examplar dưới đây:

#include <iostream> 

// culled by SFINAE if foo does not exist 
template<typename T> 
constexpr auto has_foo(T& t) -> decltype((void)t.foo, bool()) 
{ 
    return true; 
} 
// catch-all fallback for items with no foo 
constexpr bool has_foo(...) 
{ 
    return false; 
} 
//----------------------------------------------------- 

template<typename T, bool> 
struct GetFoo 
{ 
    static int value(T& t) 
    { 
     return t.foo; 
    } 
}; 
template<typename T> 
struct GetFoo<T, false> 
{ 
    static int value(T&) 
    { 
     return 0; 
    } 
}; 
//----------------------------------------------------- 

template<typename T> 
int get_foo(T& t) 
{ 
    return GetFoo<T, has_foo(t)>::value(t); 
} 
//----------------------------------------------------- 

struct Bar 
{ 
    int val; 
}; 

int main() 
{ 
    Bar b { 5 }; 
    std::cout << get_foo(b) << std::endl; 
    return 0; 
} 
+0

Nó nói 'val' man, not' foo'! – Rapptz

+0

@Rapptz - chính xác! Đó là vụ phải ** cull ** phương thức 'has_foo (true)' và sử dụng phương thức dự phòng –

+0

Ah man, xin lỗi. Tôi chỉ nhanh chóng lướt qua nó vì vậy tôi đã không hoàn toàn hiểu được câu hỏi :) – Rapptz

Trả lời

10

Vấn đề chính ở đây AFAICS là bạn đang sử dụng thời gian chạy tham chiếu như một constexpr tham số hàm. Thay thế này hoạt động tốt.

#include <iostream> 

// culled by SFINAE if foo does not exist 
template<typename T> 
constexpr auto has_foo(int) -> decltype(std::declval<T>().foo, bool()) 
{ 
    return true; 
} 
// catch-all fallback for items with no foo 
template<typename T> constexpr bool has_foo(...) 
{ 
    return false; 
} 
//----------------------------------------------------- 

template<typename T, bool> 
struct GetFoo 
{ 
    static int value(T& t) 
    { 
     return t.foo; 
    } 
}; 
template<typename T> 
struct GetFoo<T, false> 
{ 
    static int value(T&) 
    { 
     return 0; 
    } 
}; 
//----------------------------------------------------- 

template<typename T> 
int get_foo(T& t) 
{ 
    return GetFoo<T, has_foo<T>(0)>::value(t); 
} 
//----------------------------------------------------- 

struct Bar 
{ 
    int val; 
}; 
struct Foo { 
    int foo; 
}; 

int main() 
{ 
    Bar b { 5 }; 
    Foo f { 5 }; 
    std::cout << get_foo(b) << std::endl; 
    std::cout << get_foo(f) << std::endl; 
    return 0; 
} 
+0

Theo dõi nếu tôi có thể. Bạn đang sử dụng tham số 'int' (và giá trị truyền của kiểu này) để phân biệt giữa bắt tất cả và nắm bắt được điều này. Hãy nói rằng tôi có một yêu cầu rằng tôi sẽ có giá trị thành viên 'foo' hoặc' bar' (theo thứ tự đó). Phương pháp này có phù hợp với nhiệm vụ này hay không hoặc tuyến đường khác nên được thực hiện? –

+0

@RedXIII: Có, phương pháp này phù hợp. Trong trường hợp bắt tất cả, chỉ cần sử dụng '.bar'. Nếu có '.foo', phương thức đầu tiên được sử dụng. Nếu không có '.foo' nhưng có một' .bar', catch-all được chọn và nó biên dịch. Nếu không có, tất cả các lựa chọn được chọn và nó không biên dịch. – MSalters

+1

@RedXIII: Có, bạn có thể mở rộng nó đến nhiều hơn một bằng cách sử dụng câu nói, 'int' và' long', (thứ tự 'int' ->' long' -> '...') nhưng bạn cũng có thể tùy chỉnh đến một sở thích tùy ý bằng cách sử dụng các lớp cơ sở. – Puppy