2013-06-18 11 views
6

Tôi có đoạn mã sau:Tại sao tôi cần xác định loại đối số mẫu của một hàm mẫu ở đây?

template <typename T> 
void f1(T t) 
{ 
    std::cout << "f1(" << t << ") called." << endl; 
} 

template <typename T> 
void f2(T t) 
{ 
    std::cout << "f2(" << t << ") called." << endl; 
} 

template <typename F, typename T> 
void call(F && f, T t) 
{ 
    f(t); 
} 

template <typename T> 
void foo(T t) 
{ 
    call(f1<T>, t); // Why is <T> necessary? 
         // f1(t) is a valid expression! 
    call(f2<T>, t); 
} 

void bar() 
{ 
    foo(1); 
} 

Trong chức năng foo() tôi cần phải xác định mẫu tranh luận, mặc dù f1(t) là một biểu thức hợp lệ. Đó là kinda phá hủy một số khả năng trong mã của tôi. Câu hỏi của tôi:

  1. Tại sao tôi cần chỉ định đối số mẫu?
  2. Tôi làm cách nào để khắc phục giới hạn đó? (Cho phép C++ 11 hoặc C++ 14).

(BTW: Tôi hiện đang sử dụng Visual Studio 2010 và tôi nhận được lỗi C2896, nếu tôi rời khỏi <T> ra.)

Trả lời

11

f1 không phải là một chức năng, đó là một bản mẫu. Bạn không thể chuyển mẫu dưới dạng đối số hàm.

f1<T> là một hàm, vì vậy nó có thể được chuyển.

1

f1(t) không phải là biểu thức hợp lệ vì không có hàm f1. Chỉ có một mẫu có tên là f1 mà từ đó một hàm f1<T> có thể được tạo vào thời gian biên dịch.

Giới hạn mà bạn nói đến là hậu quả trực tiếp của việc kiểm tra loại biên dịch. Nếu bạn biết loại đối số của foo tại thời gian biên dịch, không có giới hạn, vì bạn không thể thêm nó một cách dễ dàng. Nếu bạn không biết loại đối số, bạn có thể phải sử dụng mô hình lớp dẫn xuất thay vì một ý tưởng định dạng mẫu.

+1

Chắc chắn 'f1 (t)' là một biểu thức hợp lệ bên trong 'foo()'. Nó biên dịch tốt. –

+0

Trích tham số mẫu không hoạt động theo các mẫu cụ thể, có lẽ điều này trả lời câu hỏi của bạn: http://stackoverflow.com/questions/1268504/why-is-the-template-argument-deduction-not-working-here – urzeit

+0

@RalphTandetzky: Nó không quan trọng những gì * có thể * là một biểu thức hợp lệ vì nó được sử dụng bên trong 'foo'. Các mẫu là * không phải macro *; họ không chỉ mất bất cứ điều gì và dán các thẻ với nhau sau đó. Hàm mẫu của bạn có 'F && f'. Đó là một tham số hàm; nó phải là * giá trị *, có * loại *. Các mẫu không phải là các giá trị cũng không có các loại. Vì vậy, chúng không thể được chuyển thành các tham số hàm. –

0

Giải thích tại sao nó không hoạt động được đưa ra bởi AngewUrzeit.

Điều tôi đang cố cung cấp là giải pháp có thể xảy ra cho sự cố của bạn. Tuy nhiên, tôi không thể nói chính xác liệu điều này có phù hợp hay không cho trường hợp của bạn vì OP cung cấp thông tin hạn chế về các yêu cầu thiết kế cụ thể của bạn.

Bước đầu tiên là chuyển f1f2 vào lớp functor mẫu:

template <typename T> 
class f1 { 
public: 
    void operator()(T t) { 
     std::cout << "f1(" << t << ") called." << std::endl; 
    } 
}; 

(. Tương tự như vậy cho f2) Bằng cách này, bạn có thể vượt qua f1 (và không f1<T>) như là một mẫu template tham số để call hiện được xác định theo cách này:

template <template <typename> class F, typename T> 
void call(T t) 
{ 
    F<T> f; 
    f(t); 
} 

Lưu ý rằng F là thông số mẫu mẫu mà bi nds đến một lớp mẫu lấy một tham số kiểu mẫu (ví dụ: f1).

Cuối cùng, foo trở này:

template <typename T> 
void foo(T t) 
{ 
    call<f1>(t); 
    call<f2>(t); 
} 

bar còn lại như trước đây.

3

Bạn có thể thử bọc các chức năng được tạo khuôn mẫu f1 và f2 trong các lớp không được tạo mẫu và các phiên bản truyền (hoặc thậm chí cả các loại) xung quanh, ví dụ:

struct F1 
{ 
    template <typename T> 
    void operator()(T t) const 
    { 
    std::cout << "F1::operator()(" << t << ") called" << std::endl; 
    } 
}; 

struct F2 
{ 
    template <typename T> 
    void operator()(T t) const 
    { 
    std::cout << "F2::operator()(" << t << ") called" << std::endl; 
    } 
}; 

template <typename F, typename T> 
void call(F && f, T t) 
{ 
    f(t); 
} 

template <typename T> 
void foo(T t) 
{ 
    static const F1 f1; 
    static const F2 f2; 
    call(f1, t); 
    call(f2, t); 
} 

void bar() 
{ 
    foo(1); 
} 

trong đó sản xuất đầu ra:

F1 :: operator() (1) gọi là

F2 :: operator() (1) gọi là

+0

+1 Đây là một biến thể tốt đẹp của cùng một ý tưởng mà tôi đã sử dụng trong câu trả lời của mình. Tuy nhiên, nó có thể hữu ích (nó phụ thuộc vào các yêu cầu của OP) để thực hiện 'call', thay vì nhận một' F', hãy tạo một instantiation được xây dựng mặc định cục bộ của nó. Sau đó, trong 'foo' chúng ta có thể sử dụng' gọi (t); '. –

8

1. Tại sao tôi cần phải xác định đối số mẫu?

Vâng, f1 không phải là đối tượng mà là mẫu chức năng. Bạn chỉ có thể truyền đối tượng cho hàm.

2. Tôi làm cách nào để khắc phục giới hạn đó? (Cho phép C++ 11 hoặc C++ 14).

Sử dụng đối tượng có templated operator(). Chỉ cần thay thế định nghĩa của f1() với

struct { template <typename T> void operator()(T t) 
{ 
    std::cout << "f1(" << t << ") called." << endl; 
} } f1; 

và tương tự như vậy cho f2(). Trong C++ 14 bạn có thể viết nó thậm chí còn đẹp hơn

static const auto f1 = [](auto t) 
{ 
    std::cout << "f1(" << t << ") called." << endl; 
}; 
+0

Nhờ tất cả những người trả lời khác cho những đầu vào đầy cảm hứng của họ. –

1

Có một cách để bắt chước đi mẫu chức năng (hoặc bộ quá tải) xung quanh như các giá trị hạng nhất: "cụ thể hóa" chúng bằng cách biến chúng thành đối tượng chức năng. Mã của bạn có thể được viết lại như thế này:

struct F1 
{ 
    template <typename T> 
    void operator()(T t) 
    { 
    std::cout << "f1(" << t << ") called." << endl; 
    } 
} f1; 

struct F2 
{ 
    template <typename T> 
    void operator()(T t) 
    { 
    std::cout << "f2(" << t << ") called." << endl; 
    } 
} f2; 

// Note that this function didn't change at all! 
template <typename F, typename T> 
void call(F && f, T t) 
{ 
    f(t); 
} 
// Neither did this, expect that now you don't need the <T> 
template <typename T> 
void foo(T t) 
{ 
    call(f1, t); 
    call(f2, t); 
} 

void bar() 
{ 
    foo(1); 
    foo(3.14); 
    foo("Hello World"); 
}