10

マニピュレータのリストを関数に渡したいのですが、次のようになります。

void print(const vector<std::smanip>& manips) {
  // ...
  for (auto m : manips)
    cout << m;
  // ...
}

これは、理想的には次のようなコードで呼び出されます。

some_object.print({std::fixed, std::setprecision(2)}));

g ++ 4.7.0によると:

error: ‘std::smanip’ has not been declared

どうやら、smanip標準では実際には定義されておらず、C++11コンパイラはマニピュレータのタイプに明示的な名前を指定する必要はありません。次のように、既知のマニピュレータからリーチしてタイプを宣言してみました。

typedef decltype(std::fixed) manip;

これにより、次のような多くの新しいエラーメッセージが表示されました。

error: ‘const _Tp* __gnu_cxx::new_allocator< <template-parameter-1-1> 
>::address(__gnu_cxx::new_allocator< <template-parameter-1-1> >::const_reference)
const [with _Tp = std::ios_base&(std::ios_base&); __gnu_cxx::new_allocator<
<template-parameter-1-1> >::const_pointer = std::ios_base& (*)(std::ios_base&);
__gnu_cxx::new_allocator< <template-parameter-1-1> >::const_reference =
std::ios_base& (&)(std::ios_base&)]’ cannot be overloaded

今すぐあきらめるべきですか、それともこれを行う方法はありますか?

4

4 に答える 4

7

出力マニピュレータは、インスタンス化os << mのために定義された単純なタイプです。basic_ostreamマニピュレータは関数(のoperator<<オーバーロードの対象basic_ostream)にすることができますが、それ自体を定義する任意のタイプにすることもできますoperator<<。そのoperator<<ため、適切なbasic_ostreamインスタンス化のためにをキャプチャするために型消去を実行する必要があります。これを行う最も簡単な方法はstd::function、ラムダを使用することです。

#include <iostream>
#include <iomanip>
#include <functional>
#include <vector>

template<typename S>
struct out_manipulator: public std::function<S &(S &)> {
   template<typename T> out_manipulator(T &&t): std::function<S &(S &)>(
      [=](S &i) -> S &{ return i << t; }) {}
   template<typename T> out_manipulator(T *t): std::function<S &(S &)>(
      [=](S &i) -> S &{ return i << t; }) {}    // for g++
   template<typename U> friend U &operator<<(U &u, out_manipulator &a) {
      return static_cast<U &>(a(u));
   }
};

void print(const std::vector<out_manipulator<std::ostream>> &manips) {
   for (auto m: manips)
      std::cout << m;
}

int main() {
   print({std::fixed, std::setprecision(2)});
   std::cout << 3.14159;
}
于 2013-02-05T09:28:44.507 に答える
6

マニピュレータはほぼ任意のタイプを持つことができるため、テンプレートを使用してそれらを処理する必要があります。固定型のポインタまたは参照を使用してそれらにアクセスするには、これらすべてのテンプレートに共通の基本クラスを使用する必要があります。この種のポリモーフィズムは、ポインターと参照に対してのみ機能しますが、特にこれらを包含に格納するために、値のセマンティクスが必要になる可能性があります。したがって、最も簡単な方法は、shared_ptrメモリ管理を処理し、別のクラスを使用して、ユーザーからすべての醜い詳細を隠すことです。

結果は次のようになります。

#include <memory>
#include <iostream>

// an abstract class to provide a common interface to all manipulators
class abstract_manip {
public:
  virtual ~abstract_manip() { }
  virtual void apply(std::ostream& out) const = 0;
};

// a wrapper template to let arbitrary manipulators follow that interface
template<typename M> class concrete_manip : public abstract_manip {
public:
  concrete_manip(const M& manip) : _manip(manip) { }
  void apply(std::ostream& out) const { out << _manip; }
private:
  M _manip;
};

// a class to hide the memory management required for polymorphism
class smanip {
public:
  template<typename M> smanip(const M& manip)
    : _manip(new concrete_manip<M>(manip)) { }
  template<typename R, typename A> smanip(R (&manip)(A))
    : _manip(new concrete_manip<R (*)(A)>(&manip)) { }
  void apply(std::ostream& out) const { _manip->apply(out); }
private:
  std::shared_ptr<abstract_manip> _manip;
};

inline std::ostream& operator<<(std::ostream& out, const smanip& manip) {
  manip.apply(out);
  return out;
}

これにより、名前空間にわずかな変更を加えた後、コードが機能します。

void print(const std::vector<smanip>& manips) {
  for (auto m : manips)
    std::cout << m;
}

int main(int argc, const char** argv) {
  print({std::fixed, std::setprecision(2)});
}
于 2013-02-05T09:09:35.780 に答える
1

マニピュレータは関数であるため、そのシグネチャに依存します。つまり、同じシグネチャを持つマニピュレータのベクトルを作成できます。

例えば ​​:

#include <iomanip>
#include <vector>
#include <iostream>

typedef std::ios_base& (*manipF)( std::ios_base& );

std::vector< manipF > getManipulators()
{
    std::vector< manipF > m =
    {
        std::showpos,
        std::boolalpha
    };
    return m;
}

int main()
{
  auto m = getManipulators();

  for ( auto& it : m )
  {
    std::cout<<it;
  }
  std::cout<<"hi " << true << 5.55555f << std::endl;
}

別の方法はラムダを使用することです:

#include <iomanip>
#include <vector>
#include <iostream>
#include <functional>

typedef std::function< std::ios_base& ( std::ios_base& ) > manipF;

std::vector< manipF > getManipulators()
{
    std::vector< manipF > m =
    {
        std::showpos,
        std::boolalpha,
        [] ( std::ios_base& )->std::ios_base&
        {
          std::cout << std::setprecision( 2 );
          return std::cout;
        }
    };
    return m;
}

int main()
{
  auto m = getManipulators();

  for ( auto& it : m )
  {
    it(std::cout);
  }
  std::cout<<"hi " << true << 5.55555f << std::endl;
}
于 2013-02-05T08:31:34.760 に答える
0

標準のC++17ソリューションは、std::tupleに基づいている可能性があります。これがPOCです

int main()
{
// quick p.o.c.
auto ios_mask_keeper = [&](auto mask) {
    // keep tuple here
    static auto mask_ = mask;
    return mask_;
};

// make required ios mask and keep it
auto the_tuple = ios_mask_keeper(
    // please note we can mix ios formaters and iomanip-ulators
    std::make_tuple(std::boolalpha, std::fixed, std::setprecision(2) ) 
);

// apply iomanip's stored in a tuple
std::apply([&](auto & ...x) 
{
    // c++17 fold 
    (std::cout << ... << x);
}, the_tuple);

return 0;

}

タプルをクラスなどに保持することができます。MSVCCLコンパイラバージョン19.14.26431.0と、clangを使用する必須のワンドボックスの両方で動作します。

于 2018-07-07T18:44:43.790 に答える