如何在C++ 17中打印一个模板以控制?

发布于 2025-02-12 02:38:43 字数 1108 浏览 1 评论 0 原文

我有此基本模板类:

template<typename Type>
struct InputBase {
    virtual void DisplaySingleResult(Type const& res) const {
        std::cout << res << std::endl;
    }
};

因此,下面的代码不会编译,因为 vector 没有&lt;&lt;&lt; 运算符

struct Input : InputBase<std::vector<std::vector<int>>> {
    void DisplaySingleResult(std::vector<std::vector<int>> const& results) const override {
        for (std::vector<int> const& result : results) {
            std::cout << "[" << result[0] << "," << result[1] << "]";
        }
        std::cout << std::endl;
    }
};

在编译时,方法 inputBase :: displaySingleresult 无法编译,因为它使用 vector type。

解决方法是使虚拟 inputBase :: displaysingleresult 方法纯虚拟。

但是,我想在我的基类中具有 displaysingleresult 方法,并具有相同的签名,以避免重写所有其他派生的类。如何在C ++ 17中做到这一点?

解决方案试验: inputbase :: displaysingleresult ,检查模板类型是否具有&lt;&lt;&lt; 运算符。

I have this base template class:

template<typename Type>
struct InputBase {
    virtual void DisplaySingleResult(Type const& res) const {
        std::cout << res << std::endl;
    }
};

Therefore, this code below doesn't compile, because vector doesn't have a << operator

struct Input : InputBase<std::vector<std::vector<int>>> {
    void DisplaySingleResult(std::vector<std::vector<int>> const& results) const override {
        for (std::vector<int> const& result : results) {
            std::cout << "[" << result[0] << "," << result[1] << "]";
        }
        std::cout << std::endl;
    }
};

When compiling, the method InputBase::DisplaySingleResult fails to compile because it uses vector type.

A workaround is to make the virtual InputBase::DisplaySingleResult method pure virtual.

However, I want to have a DisplaySingleResult method in my base class, with the same signature, to avoid rewriting all other derived classes. How to do that in C++17?

Solution tentative: in InputBase::DisplaySingleResult, check if the template Type has a << operator before using it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

孤芳又自赏 2025-02-19 02:38:43

我不建议为 std :: Ostream&amp;提供超载。操作员&lt;&lt;(std :: ostream&amp;,const std :: vector&lt; t&gt;&amp;&amp;)直接(不确定是否允许它)。但是间接。

而不是调用 std :: Cout&lt;&lt; res&lt;&lt; std :: endl; 您可以调用一个函数:

template<typename Type>
void print_result(const Type& res) { std::cout << res << std::endl; }

template<typename Type>
struct InputBase {
    virtual void DisplaySingleResult(Type const& res) const {
        print_result(res);
    }
};

现在您可以为没有运算符&lt;&lt;&lt; 提供

void print_result(const std::vector<int>& v) { for (const auto& e : v) print_result(e); } 

超载要考虑制作 print_result 类模板而不是函数模板。例如,当 t 具有 begin> begin() and end(end eend() end()。

I do not recommend to provide an overload for std::ostream& operator<<(std::ostream&,const std::vector<T>&) directly (not sure if it is even allowed). But indirectly.

Instead of calling std::cout << res << std::endl; you can call a function:

template<typename Type>
void print_result(const Type& res) { std::cout << res << std::endl; }

template<typename Type>
struct InputBase {
    virtual void DisplaySingleResult(Type const& res) const {
        print_result(res);
    }
};

Now you can provide overloads for any type that does not have a operator<<, eg

void print_result(const std::vector<int>& v) { for (const auto& e : v) print_result(e); } 

To be more flexible with specializations you might want to consider to make print_result a class template rather than function template. For example there could be a specialization that calls print_result on the elements when T has begin() and end().

独自唱情﹋歌 2025-02-19 02:38:43

您可以在编译时测试是否适当支持类型:

  1. 如果有合适的输出操作员已经使用它(此测试可以确保您不使用迭代器,如果类型可以直接输出,例如,例如 std :: String )。
  2. 否则,如果类型具有开始结束函数使用这些功能。
  3. 否则我们处于错误条件!

这可能如下:

template<typename Type>
struct InputBase
{
    static void displaySingleResult(Type const& res)
    {
        if constexpr (decltype(has_operator_out(res))::value)
        {
            std::cout << res;
        }
        else if constexpr
        (
            decltype(has_begin(res))::value && decltype(has_end(res))::value
        )
        {
            // your own desired formatting, e.g.:
            std::cout << '[';
            for(auto& r : res)
            {
                std::cout << r << ' ';
            }
            std::cout << ']';
        }
        else
        {
            // error handling:
            std::cout << "<invalid!>";
        }
    }

private:
    template <typename T>
    static auto has_operator_out(T& t)
        -> decltype(std::cout << t, std::true_type());
    static std::false_type has_operator_out(...);
    template <typename T>
    static auto has_begin(T& t)
        -> decltype(t.begin(), std::true_type());
    static std::false_type has_begin(...);
    template <typename T>
    static auto has_end(T& t)
        -> decltype(t.end(), std::true_type());
    static std::false_type has_end(...);
};

递归调用该功能也允许打印嵌套容器(感谢对于提示):

std::cout << '[';
for(auto& r : res)
{
    InputBase<std::remove_cv_t<std::remove_reference_t<decltype(r)>>>
        ::displaySingleResult(r);
    std::cout << ' ';
}
std::cout << ']';

请注意:

  1. ... <代码>静态 在任何地方都不使用此。
  2. ...定义运算符&lt;&lt; 更喜欢C ++。
  3. ...您可能会提供进一步的测试或根据您的需求调整现有测试,例如使用 std :: begin std :: end end 而不是使用。
  4. ...对于这些测试还有其他模式,您可能想与之一起玩;)

You could test at compile-time if the type is supported appropriately:

  1. If there's a suitable output operator available already use it (this test assures that you don't use iterators if the type can be output directly, like e.g. std::string).
  2. Otherwise if the type has begin and end functions use these.
  3. Otherwise we are in an error condition!

This might look as follows:

template<typename Type>
struct InputBase
{
    static void displaySingleResult(Type const& res)
    {
        if constexpr (decltype(has_operator_out(res))::value)
        {
            std::cout << res;
        }
        else if constexpr
        (
            decltype(has_begin(res))::value && decltype(has_end(res))::value
        )
        {
            // your own desired formatting, e.g.:
            std::cout << '[';
            for(auto& r : res)
            {
                std::cout << r << ' ';
            }
            std::cout << ']';
        }
        else
        {
            // error handling:
            std::cout << "<invalid!>";
        }
    }

private:
    template <typename T>
    static auto has_operator_out(T& t)
        -> decltype(std::cout << t, std::true_type());
    static std::false_type has_operator_out(...);
    template <typename T>
    static auto has_begin(T& t)
        -> decltype(t.begin(), std::true_type());
    static std::false_type has_begin(...);
    template <typename T>
    static auto has_end(T& t)
        -> decltype(t.end(), std::true_type());
    static std::false_type has_end(...);
};

Recursively calling the function instead comes allows to print nested containers as well (thanks 463035818_is_not_a_number for the hint):

std::cout << '[';
for(auto& r : res)
{
    InputBase<std::remove_cv_t<std::remove_reference_t<decltype(r)>>>
        ::displaySingleResult(r);
    std::cout << ' ';
}
std::cout << ']';

Note that:

  1. ... the function can be static as this is not used anywhere.
  2. ... defining an operator<< instead would be more C++ like.
  3. ... you might provide further tests or adjust the existing ones to your needs, e.g. for using std::begin and std::end instead.
  4. ... there are other patterns possible for these tests, you might want to play around a bit with ;)
药祭#氼 2025-02-19 02:38:43

如果您需要具有 displaysingleresult()是虚拟的,那么一种典型的方法是委派给非虚拟函数,这可以是模板:

virtual void DisplaySingleResult(Type const& res)
{
    DisplaySingleResultImpl(res);
}

那么您可以templetize displaysingleresultimpl()。现在,在许多情况下,您不想在基类(或只想提供典型的实现)中这样做 - 在这些情况下,CRTP想到:

template<typename Type, typename Derived>
struct InputBase {
    virtual void DisplaySingleResult(Type const& res) const {
        static cast<const Derived&>(*this).DisplaySingleResultImpl(res);
    }
};

template<typename Type, typename Derived>
struct InputBaseWithCoutPrint : InputBase<Type, Derived> {
    void DisplaySingleResultImpl(Type const& res) const {
        std::cout << res << std::endl;
    }
};

CRTP是一个非常强大的工具,不要过度使用它。

If you need to have DisplaySingleResult() to be virtual, a typical way is to delegate to a non-virtual function, which can be a template:

virtual void DisplaySingleResult(Type const& res)
{
    DisplaySingleResultImpl(res);
}

Then you can templetize DisplaySingleResultImpl(). Now, in many cases, you don't want to do that in the base class (or only want to provide a typical implementation) - in these cases, CRTP comes to mind:

template<typename Type, typename Derived>
struct InputBase {
    virtual void DisplaySingleResult(Type const& res) const {
        static cast<const Derived&>(*this).DisplaySingleResultImpl(res);
    }
};

template<typename Type, typename Derived>
struct InputBaseWithCoutPrint : InputBase<Type, Derived> {
    void DisplaySingleResultImpl(Type const& res) const {
        std::cout << res << std::endl;
    }
};

CRTP is a very powerful tool, don't overuse it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文