2015-05-29 18 views
9

Tôi muốn có một mẫu với một giá trị lồng nhau mà phải được khởi tạo bởi một hàm khởi tạo đưa ra:Chuyên mẫu với con trỏ hàm, mà phụ thuộc vào tham số mẫu

template <typename T, T(INIT)()> struct Foo 
{ 
    T value = INIT(); 
}; 

Nó có thể được sử dụng theo cách này:

// Some random type only instanceable through factory() 
struct Bar 
{ 
    int bar{}; 
private: 
    // The only way to create a Bar is through factory() 
    friend Bar factory(); 
    Bar() {}; 
}; 

Bar factory() { return {}; } 

Foo<Bar, factory> foo; 

Nhưng, nếu không có chức năng được cung cấp, các mẫu nên cố gắng mặc định khởi tạo các giá trị lồng nhau, vì vậy tôi đã cố gắng để chuyên template:

template <typename T> struct Foo<T, nullptr> 
{ 
    T value{}; 
}; 

Ý tưởng là để sử dụng nó theo cách này:

struct Baz{}; 

Foo<Bar, factory> foo; // Nested Bar have Bar::bar initialized through factory function. 
Foo<Baz>   baz; // No factory function needed, nested Baz default-initialized. 

Nhưng tôi chỉ phát hiện ra rằng mẫu loại chuyên môn hóa một phần không thể dựa trên các loại mẫu khác, lỗi tôi nhận được dán bên dưới:

error: type 'T (*)()' of template argument 'nullptr' depends on a template parameter template struct Foo


Có cách nào để đạt được mục tiêu của mình không? Sẽ tốt hơn nếu nó hoạt động với các biến mẫu:

template <typename T, T(INIT)()> T Foo = INIT(); 
template <typename T>   T Foo<T, nullptr>{}; 

Câu hỏi bổ sung: Tại sao một phần chuyên môn không thể phụ thuộc vào thông số mẫu? Lý do đằng sau hạn chế này là gì?

Trả lời

2

Nếu nó chỉ về việc khởi tạo mặc định nếu tham số mẫu thứ hai bị thiếu, bạn có thể cung cấp chức năng khởi tạo mặc định theo khuôn mẫu làm tham số mặc định như.

template<typename T> 
    T do_default_assign() { 
     return T(); 
    };                  

template <typename T, T (INIT)() = do_default_assign<T> > struct Foo 
    { 
     T value = INIT(); 
    }; 

này tuy nhiên bị một không cần thiết "trở lại theo giá trị" và hoạt động chuyển nhượng mà có thể tốn kém hoặc không thể đối với một số T.

+0

Đã chấp nhận điều này và không có ai từ @ Jarod42 vì nó được trả lời 1 giây trước! –

3

Đối với trường hợp của bạn, bạn có thể sử dụng:

template <typename T> 
T default_construct() { return T{}; } 

template <typename T, T(INIT)() = &default_construct<T>> 
struct Foo 
{ 
    T value = INIT(); 
}; 

Và sau đó sử dụng nó như:

Foo<int> f; 
Foo<int, bar> b; 

Live demo

2

Bạn có thể định nghĩa một hàm constructor mẫu mà sẽ khởi tạo một giá trị kiểu Type và sau đó sử dụng nó như một constructor mặc định:

template<typename Type, typename... Args> 
Type constructor(Args... args) { 
    return Type(std::forward<Args>(args)...); 
} 

và sau đó sử dụng nó như là mẫu mặc định tham số cho hàm:

template <typename T, T(INIT)() = constructor<T>> struct Foo 
{ 
    T value = INIT(); 
}; 

Live demo

+0

Cách tiếp cận này sẽ rất tuyệt vì nó cho phép truyền bất kỳ tham số nào tới hàm tạo, nhưng việc cung cấp 'constructor' không tương thích với' T (INIT)() 'là không thể vì vậy cuối cùng: chuyển tiếp tất cả các hàm' Args' tới 'constructor 'không có ý nghĩa: ( –

+0

@PaperBirdMaster Đúng, nhưng bây giờ bạn có một' constructor' tổng quát hơn là một hàm hữu ích hoạt động trên bất kỳ hàm tạo nào của bất kỳ kiểu nào (cho các mục đích khác), thay vì một 'default_construct' được mã hóa cứng hoặc 'do_default_assign' như đã đề xuất khác. – Shoe

Các vấn đề liên quan