web-dev-qa-db-ja.com

反復子の範囲が必要な関数に単一のパラメーターを渡す

1つ以上のパラメーター(ファイル名など)を受け入れる関数を考えます。汎用性を高めるために、一般的なイテレータの範囲で記述すると便利です。

template<class Iter>
void function(Iter first, Iter last)
{
  // do something
}

これで、引数の格納方法に関係なく、次の方法で呼び出すことができます。

WhateverContainer container;
function(std::begin(container), std::end(container));

たとえば、STLはこのパラダイムに大きく依存しています。

ここで、コンテナーに格納されていない単一の引数で関数を呼び出すことを想像してください。もちろん書くことができます:

const int value = 5;
std::vector<int> vec(1, value);
function(std::begin(vec), std::end(vec));

しかし、この解決策は不器用で無駄に思えます。

質問:単一の変数のイテレータ範囲互換の表現を作成する、よりオーバーヘッドの少ない方法はありますか?

21
piripiri

一度にポインタを使用できます:

function(&value, &value + 1);

一般的なコードでは、std::addressof単項演算子の代わりに&は、パラノイアのレベルに応じて、多少安全です。

もちろん、これをオーバーロードでラップして簡単に使用できます。

template <class T>
decltype(auto) function (T &&e) {
    auto p = std::addressof(e);
    return function(p, p + 1);
}
33
Baum mit Augen

[[expr.unary.op]/ ごとに1つの要素の配列のように扱うことができます。

function(&value, &value + 1);

ポインター演算([expr.add])および比較([expr.rel]、[expr.eq])の目的で、この方法でアドレスが取得される配列要素ではないオブジェクトは、配列に属すると見なされますタイプTの要素が1つあります。

13
chris

また、overload関数テンプレートfunction単一要素の範囲

template<typename Iter>
void function(Iter first) {
    return function(first, std::next(first)); // calls your original function
}

このようにして、元の関数functionは反復子の範囲との互換性を維持します。ただし、空の範囲でこのオーバーロードを使用すると、未定義の動作が発生することに注意してください。


単一の要素valueの場合、上記のオーバーロードを使用できます。

function(&value); // calls overload

演算子&はオーバーロードされる可能性があるため、すでに この回答 で説明されているように、std::addressofの代わりに&の使用も検討してください。


単一の要素で構成される範囲の場合、上記のオーバーロードを使用することもできます。これには、イテレーターペアの代わりに単一のイテレーターのみが必要です。

const int value = 5;
std::vector<int> vec(1, value); // single-element collection
function(std::begin(vec)); // <-- calls overload
6
El Profesor

私はこれを2つのステップで行うと思います。

  1. イテレーターのバージョンで記述された、コンテナーを取るテンプレート関数のオーバーロードを定義します。

  2. オブジェクト参照をサイズ1の配列として扱うプロキシクラスを定義します。

c ++ 17の例:

#include <iterator>
#include <type_traits>
#include <vector>
#include <iostream>

// proxy object
template<class T>
struct object_as_container
{
    using value_type = T;
    using iterator = T*;
    using const_iterator = std::add_const_t<T>;

    object_as_container(value_type& val) : object_(val) {}

    const_iterator begin() const { return std::addressof(object_); }
    iterator begin() { return std::addressof(object_); }

    const_iterator end() const { return std::next(begin()); }
    iterator end() { return std::next(begin()); }

private:
    value_type& object_;
};

// our function in terms of iterators    
template<class Iter> void func(Iter first, Iter last)
{
    while(first != last)
    {
        std::cout << *first++;
    }
}

// our function in terms of containers
template<class Container> void func(Container&& cont)
{
    func(cont.begin(), cont.end());
}

int main()
{
    const int value = 5;
    func(object_as_container(value));
    func(std::vector { 1,2,3,4,5 });
}
2
Richard Hodges