c++ - 使用Concepts Lite為具有成員函數模板的類型指定概念




higher-kinded-types c++-concepts (2)

我試圖指定一個概念來約束使用Concepts Lite的具有成員函數模板的更高親密類型。 但是我無法在技術規範教程中找到一個處理概念內模板語句的子句。

這是如何完成的?

例如:假設我有一個成員函數模板F

template<class T>
struct HKT {
  template<class U> // this looks like e.g. rebind in std::allocators
  auto F(U) -> HKT<U>;
};

而現在我想指定一個概念來約束這些更高級的接吻類型:

template <template <class> class HKT, class T>
concept HKTWithTemplateMemberFunctionF {
  return requires(HKT<T> h) { // HKT<T> is a type, h is an object
    // HKT<T> needs to have a member function template that 
    // returns HTK<U> where the type U is to be deduced and
    // it can be any type (it is unconstrained)
    template<class U>  // is there a syntax for this?
    h.F(std::declval<U>()) -> HKT<U>; 
  }
}

請注意,我可以這樣做:

template <template <class> class HKT, class T, class U>
concept HKTWithTemplateMemberFunctionF {
  return requires(HKT<T> h) {
      h.F(std::declval<U>()) -> HKT<U>;
  }
}

但這意味著我需要在約束點處了解U

我真的不在乎替換給定的U失敗,儘管我可以明白為什麼這可能是一個問題:例如,應用一個約束來確保你的函數不失敗,然後失敗,導致約束得到滿足,但在實例化時間替換在成員函數模板中失敗(如果成員函數模板受到約束,會有幫助嗎?)。


讓我們從您的評論中考慮您需要的要求:

// HKT<T> needs to have a member function template that 
// returns HTK<U> where the type U is to be deduced and
// it can be any type (it is unconstrained)

雖然Concepts要求我們將約束條件放在具體類型的基礎上,但我們可以在選擇使用哪些具體類型時做出明智選擇。 U是什麼意思? 真的是任何類型的,不管怎樣? 想想你在U上的最小可能的約束條件,讓我們構建一個滿足它們的類型。 這被稱為U原型

我首先想到的“任何類型”實際上都是半規則類型。 一種默認可構造,可複制和可分配的類型。 所有正常的好東西:

namespace archetypes {
    // private, only used for concept definitions, never in real code
    struct Semiregular { };
}

archetypes::Semiregular是一個具體類型,所以我們可以用它來構建一個概念:

template <template <class> class HKT, class T>
concept bool HKTWithTemplateMemberFunctionF = 
  requires(HKT<T> h, archetypes::Semiregular r) {
    {h.F(r)} -> HKT<archetypes::Semiregular>
  };

archetypes::Semiregular是一種私人類型。 它不應該被HKT知道,所以如果hF(r)是格式良好的並且返回一個可轉換為HKT<archetypes::Semiregular> ,它幾乎肯定是一個成員函數模板。

那麼問題是,這是一個很好的原型嗎? 我們是否需要U是半規則的,還是不規則類型也會起作用? 您需要的操作越少,原型中應該出現的就越少。 也許你需要的只是U是可移動的:

namespace archetypes {
    // private, only used for concept definitions, never in real code
    struct Semiregular { };

    struct Moveable {
        Moveable() = delete;
        Moveable(Moveable&& ) noexcept(false);
        Moveable(Moveable const& ) = delete;
        ~Moveable() = default;

        Moveable& operator=(Moveable const& ) = delete;
        Moveable& operator=(Moveable&& ) noexcept(false);
    };
}

template <template <class> class HKT, class T>
concept bool HKTWithTemplateMemberFunctionF =
  requires(HKT<T> h, archetypes::Moveable m) {
    { h.F(m) } -> HKT<archetypes::Moveable>
  };

我們正在測試相同的想法 - 調用F()的類型不是眾所周知的,除了返回類型以反映它,因此要求它是一個函數模板。 但是現在我們正在給該類型提供更少的功能。 如果F()適用於任何類型 ,它將在archetypes::Moveable

繼續迭代這個想法,直到你真正將所需的功能降到最低。 也許你甚至不需要原型被破壞? 編寫原型很難,但在這種情況下,正確的做法很重要。


長話短說,現在你(我?)必須提供一個特定的U

template <template <class> class HKT, class T, class U = T>
concept HKTWithTemplateMemberFunctionF {
  return requires(HKT<T> h) { // HKT<T> is a type, h is an object
    h.F(std::declval<U>()) -> HKT<U>; 
  }
}

因為編譯器不能證明所有可能存在的成員函數模板都可以使用的U類型,也就是說,以下是無望的:

template <template <class> class HKT, class T>
concept HKTWithTemplateMemberFunctionF {
  return requires(HKT<T> h) {
    template<class U>  // for all those Us that haven't been written yet...
    h.F(std::declval<U>()) -> HKT<U>; 
  }
}

在一個假設的5分鐘概念精簡實現中,我們可以將U 稍微限制一下

template <template <class> class HKT, class T, 
          InputIterator U = InputIterator()  /* imaginary syntax */ >
concept HKTWithTemplateMemberFunctionF {
  return requires(HKT<T> h) {
      h.F(std::declval<U>()) -> HKT<U>; // Is InputIterator enough to instantiate F?
  }
}

編譯器只需要檢查InputIterator的模型是否足以實例化hF ,即使hF沒有受到約束,這也是可能的! 另外提供U只是檢查它是否模擬InputIterator ,因為InputIterator已經足夠了,所以不需要嘗試檢查U InputIterator 。 這可以用來優化編譯時的性能和......

...可能會以令人驚訝的方式與SFINAE進行交互,因為AFAIK可以有一個概念重載函數(例如用於InputIterator ),它接受除一個之外的所有輸入迭代器(SFINAE!為什麼會有人這樣做?!),因此可以通過概念檢查,但在實例化時間吹...悲傷。