簡體   English   中英

使用派生模板類和智能指針進行模板推導

[英]Template deduction with derived templated class and smart pointers

假設我有一個模板化基類,以及一個派生自它的模板化類:

template <typename T>
class Base {};

template <typename T>
class Derived : public Base<T> {};

此外,我有一個函數想要接受指向任何Base<T>或子類的共享指針,並且能夠輕松地使用T參數作為其簽名的一部分:

template <typename T>
T DoSomething(std::shared_ptr<Base<T>>);

我希望能夠使用推導的T調用它,並使用指向Base<T>或從它派生的任何內容的共享指針:

DoSomething(std::make_shared<Base<T>>());
DoSomething(std::make_shared<Derived<T>>());

當然后者不起作用,因為類型推導失敗。

如何修改DoSomething的簽名以使其工作? BaseDerived不是模板的情況下,我已經看到了很多答案,但是如果我仍然想推導出T (例如將其用作返回類型,如上所述),我不知道該怎么做.

理想情況下,這對於在重載解析時指向非派生輸入(和非共享指針)的共享指針會失敗。

您可以在函數中使用模板模板參數,然后使用static_assert來強制執行您的要求。

#include <memory>
#include <vector>

template <typename T>
class Base {};

template <typename T>
class Derived : public Base<T> {};

template <typename T, template <typename> typename U>
T DoSomething(std::shared_ptr<U<T>>) {
    static_assert(std::is_base_of_v<Base<T>, U<T>>, "Requires a std::shared_ptr to Base of class derived from Base");
    return T{};
}

int main() {
    auto foo = std::make_shared<Derived<int>>();
    auto baz = std::make_shared<Base<int>>();
    auto bar = std::make_shared<std::vector<int>>();

    DoSomething(foo);
    DoSomething(baz);
    DoSomething(bar); // Fails, std::vector<int> is not derived from Base<int>
}

編輯
如果DoSomething被重載,我們可以使用 SFINAE 來禁用它而不是static_assert 這將如下所示。

#include <memory>
#include <vector>

template <typename T>
class Base {};

template <typename T>
class Derived : public Base<T> {};

template <typename T, template <typename> typename U, std::enable_if_t<std::is_base_of_v<Base<T>, U<T>>, int> = 0>
T DoSomething(std::shared_ptr<U<T>>) {
    return T{};
}

int main() {
    auto foo = std::make_shared<Derived<int>>();
    auto baz = std::make_shared<Base<int>>();
    auto bar = std::make_shared<std::vector<int>>();

    DoSomething(foo);
    DoSomething(baz);
    DoSomething(bar); // Error, no matching function
}

盡管如此,@super 已經為這個問題提供了一個公認的答案,這里是使用 SFINAE 的一種略有不同(但可能不太優雅的方式):

#include <memory>
#include <type_traits>

template <typename T>
class Base {};

template <typename T>
class Derived : public Base<T> {};

template <template <typename> typename U, typename T>
std::enable_if_t<std::is_base_of<Base<T>,U<T>>::value, T>
DoSomething(std::shared_ptr<U<T>>)
{
    return T{};
}

int main()
{
    auto foo = std::make_shared<Base<int>>();
    auto bar = std::make_shared<Derived<int>>();
    
    DoSomething(foo);
    DoSomething(bar);

    return 0;
}

現場演示: godbolt

為什么我認為這不那么優雅? 這是因為函數的簽名發生了變化,現在將std::enable_if_t<...>作為返回類型,即使它最終被推導出為T (當然,對於有效輸入)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM