簡體   English   中英

統一指針,值和智能指針的C ++模板

[英]Unify C++ templates for pointers, values and smart pointers

我的真實例子非常大,所以我將使用簡化的例子。 假設我有一個矩形的數據類型:

struct Rectangle {
  int width;
  int height;

  int computeArea() {
    return width * height;
  }
}

另一種消耗該類型的類型,例如:

struct TwoRectangles {
  Rectangle a;
  Rectangle b;
  int computeArea() {
    // Ignore case where they overlap for the sake of argument!
    return a.computeArea() + b.computeArea();
  }
};

現在,我不想對TwoRectangles用戶設置所有權限制,所以我想把它作為模板:

template<typename T>
struct TwoRectangles {
  T a;
  T b;
  int computeArea() {
    // Ignore case where they overlap for the sake of argument! 
    return a.computeArea() + b.computeArea();
  }
};

用途:

TwoRectangles<Rectangle> x;
TwoRectangles<Rectangle*> y;
TwoRectangles<std::shared_ptr<Rectangle>> z;
// etc... 

問題是如果調用者想要使用指針,函數的主體應該是不同的:

template<typename T>
struct TwoRectangles {
  T a;
  T b;
  int computeArea() {
    assert(a && b);
    return a->computeArea() + b->computeArea();
  }
};

統一我的模板化函數的最佳方法是什么,以便最大量的代碼重用於指針,值和智能指針?

這樣做的一種方法是封裝TwoRectangles所有TwoRectangles ,如下所示:

template<typename T>
struct TwoRectangles {
  T a;
  T b;

  int computeArea() {
    return areaOf(a) + areaOf(b);
  }

private:
    template <class U>
    auto areaOf(U& v) -> decltype(v->computeArea()) {
        return v->computeArea();
    }

    template <class U>
    auto areaOf(U& v) -> decltype(v.computeArea()) {
        return v.computeArea();
    }
};

您不太可能擁有這兩個表達式都有效的類型。 但是你總是可以使用第二個參數向areaOf()添加額外的消歧。


另一種方法是利用以下事實:在標准庫中已經有一種方法可以調用函數: std::invoke() 您只需要知道基礎類型:

template <class T, class = void>
struct element_type {
    using type = T;
};

template <class T>
struct element_type<T, void_t<typename std::pointer_traits<T>::element_type>> {
    using type = typename std::pointer_traits<T>::element_type;
};

template <class T>
using element_type_t = typename element_type<T>::type;

template<typename T>
struct TwoRectangles {
  T a;
  T b;

  int computeArea() {
    using U = element_type_t<T>;
    return std::invoke(&U::computeArea, a) + 
        std::invoke(&U::computeArea, b);
  }
};

實際上我前段時間遇到了類似的問題,最終我選擇不這樣做(因為這是一個很大的變化),但它產生了一個似乎是正確的解決方案。

如果有任何間接,我想過制作一個幫助函數來訪問底層值。 在代碼中它看起來像這樣,也有一個類似於你的例子。

#include <iostream>
#include <string>
#include <memory>

namespace detail
{
    //for some reason the call for int* is ambiguous in newer standard (C++14?) when the function takes no parameters. That's a dirty workaround but it works...
    template <class T, class SFINAE = decltype(*std::declval<T>())>
    constexpr bool is_indirection(bool)
    {
        return true;
    }
    template <class T>
    constexpr bool is_indirection(...)
    {
        return false;
    }
}
template <class T>
constexpr bool is_indirection()
{
    return detail::is_indirection<T>(true);
}

template <class T, bool ind = is_indirection<T>()>
struct underlying_type
{
    using type = T;
};

template <class T>
struct underlying_type<T, true>
{
    using type = typename std::remove_reference<decltype(*(std::declval<T>()))>::type;
};

template <class T>
typename std::enable_if<is_indirection<T>(), typename std::add_lvalue_reference<typename underlying_type<T>::type>::type>::type underlying_value(T&& val)
{
    return *std::forward<T>(val);
}

template <class T>
typename std::enable_if<!is_indirection<T>(), T&>::type underlying_value(T& val)
{
    return val;
}
template <class T>
typename std::enable_if<!is_indirection<T>(), const T&>::type underlying_value(const T& val)
{
    return val;
}


template <class T>
class Storage
{
public:
    T val;
    void print()
    {
        std::cout << underlying_value(val) << '\n';
    }
};

template <class T>
class StringStorage
{
public:
    T str;
    void printSize()
    {
        std::cout << underlying_value(str).size() << '\n';
    }
};

int main()
{
    int* a = new int(213);
    std::string str = "some string";
    std::shared_ptr<std::string> strPtr = std::make_shared<std::string>(str);
    Storage<int> sVal{ 1 };
    Storage<int*> sPtr{ a };
    Storage<std::string> sStrVal{ str };
    Storage<std::shared_ptr<std::string>> sStrPtr{ strPtr };
    StringStorage<std::string> ssStrVal{ str };
    StringStorage<const std::shared_ptr<std::string>> ssStrPtr{ strPtr };

    sVal.print();
    sPtr.print();
    sStrVal.print();
    sStrPtr.print();
    ssStrVal.printSize();
    ssStrPtr.printSize();

    std::cout << is_indirection<int*>() << '\n';
    std::cout << is_indirection<int>() << '\n';
    std::cout << is_indirection<std::shared_ptr<int>>() << '\n';
    std::cout << is_indirection<std::string>() << '\n';
    std::cout << is_indirection<std::unique_ptr<std::string>>() << '\n';
}

暫無
暫無

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

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