【问题标题】:Getting the C++ compiler to reveal what a type can be converted to让 C++ 编译器显示一个类型可以转换成什么
【发布时间】:2013-03-06 16:45:18
【问题描述】:

下面是一个有缺陷的(和简化的)模板函数,它期望在模板 arg 上工作,该模板 arg 可以转换为预定义数量的类型之一。

刚好有2种,不过冷多了。

void do_something_type_specific( const int &unused ) { std::cout << 'i'; }
void do_something_type_specific( const std::string &unused ) { std::cout << 's'; }

template< typename Iterator >
void perform_work_on_a_range( Iterator begin, Iterator end )
{
    do_something_type_specific( *begin );
    // Perhaps more code...
}

这恰好在我的环境中产生了预期的结果。 如果 *Iterator 生成的类型可以完全转换为其中一个选项,则模板实例将成功编译。

但是,此代码不必要地要求执行转换,尽管unused 未被使用,但begin == end 时仍有UB。

如何在没有这些问题的情况下在 C++03 中实现这种行为?

【问题讨论】:

  • 我认为这可能与 SFINAE 一起完成。
  • 据我所知,C++03 只提供了一个“简单”的运行时,没有办法看到一个类型可以转换成什么。它是一种静态语言,除了一点运行时多态性。
  • @bash.d 类型可以转换为静态信息(请记住,C++ 是一种静态语言!)。我不明白为什么运行时会参与其中。
  • @bash.d:但是他把类型作为模板参数,所以他可以。
  • 这是您要找的吗? liveworkspace.org/code/3A44Ws$7

标签: c++ templates c++03


【解决方案1】:

您可以尝试使用std::iterator_traits<>,而不是取消引用我在begin == end 时导致未定义行为的迭代器。例如:

#include <iterator>
#include <string>
#include <cstdio>

void do_something_type_specific(std::string const&) { printf("%s\n", __PRETTY_FUNCTION__); }
void do_something_type_specific(int const&) { printf("%s\n", __PRETTY_FUNCTION__); }

template<class T>
struct ProduceValue
{
    static T value;
};

template<class T>
T ProduceValue<T>::value;

// Specializations for types that can't be default constructed or must be initialized.
template<>
char* ProduceValue<char*>::value = "";

template< typename Iterator >
void perform_work_on_a_range( Iterator begin, Iterator end )
{
    typedef typename std::iterator_traits<Iterator>::value_type value_type;
    do_something_type_specific(ProduceValue<value_type>::value);
}

int main() {
    char** p = 0;
    perform_work_on_a_range(p, p);

    long* q = 0;
    perform_work_on_a_range(q, q);
}

输出:

void do_something_type_specific(const string&)
void do_something_type_specific(const int&)

唯一的不便之处在于 ProduceValue&lt;T&gt; 必须专门用于无法默认构造或因其他原因(如 char*)而必须初始化的类型。

【讨论】:

  • 我是专家,我什至不知道你怎么可能用它来避免 UB。
  • @DeadMG 你基本上只是把你的专家头衔放在网上,就像所有的一样)
  • @DeadMG 例如,您可以使用iterator_traits&lt;&gt;::value_typeboost::is_convertible 编写一个元函数来确定值类型是否可转换,而不是取消引用迭代器。或者可以使用(value_type *)0 指针调用函数。
  • @DeadMG 您可以使用std::iterator_traits&lt;Iterator&gt;::value_type 调用适当的特定于类型的函数。
  • @DeadMG 彻底改变了功能 - 目标函数不使用参数,那么传递该类型的任何值有什么问题? 改用一些随机的糟糕的全局变量 - 只要它不影响程序其他部分的运行,请问在实践中有什么问题?
【解决方案2】:

有一个boost::is_convertible 元函数可用于确定是否可以将类型 T 转换为其他类型 U。

其次,对于begin == end,只需插入运行时检查即可。

【讨论】:

  • “对于开始 == 结束,只需插入一个运行时检查”会改变行为,不是吗?那么应该调用哪个do_something_type_specific
  • @DrewDormann:当begin == end 时,您要求它没有UB。那就是改变行为。所以符合要求。不检查就无法知道begin == end
  • 在这种情况下应该调用哪个do_something_type_specific()
  • @DrewDormann:我怎么会知道?为空范围定义此函数的语义是您的问题。
【解决方案3】:

问题中的问题代码试图利用模板参数函数参数的特性。

函数参数允许类型转换,但需要类型的实例化。模板参数不需要实例化,也不需要进行类型转换。

下面的模式使用 Boost 的 enable_ifis_convertible 允许编译器选择模板函数,就好像模板参数支持与函数参数相同的类型转换规则。(感谢@dhavenith建议)

#include <boost/utility.hpp>
#include <boost/type_traits.hpp>

// enable_if_c makes the return type either void or a Substitution Failure.
template < typename T>
typename boost::enable_if_c<boost::is_convertible<T,int>::value>::type
do_something_type_specific()
{
  std::cout << 'i';
}

template < typename T>
typename boost::enable_if_c<boost::is_convertible<T,std::string>::value>::type
do_something_type_specific()
{
  std::cout << 's';
}

template< typename Iterator >
void perform_work_on_a_range( Iterator begin, Iterator end )
{
    // This code is from @MaximYegorushkin's answer.  Vote him up :)
    typedef typename std::iterator_traits<Iterator>::value_type value_type;
    do_something_type_specific<value_type>();
    // Perhaps more code...
}

这已通过@MaximYegorushkin 的示例主程序进行了验证。

int main() {
    char** p = 0;
    perform_work_on_a_range(p, p);

    long* q = 0;
    perform_work_on_a_range(q, q);
}

输出:

si

【讨论】:

    【解决方案4】:

    您可能想要这样做:

    template <typename T>
    void do_something_type_specific() {}
    template <>
    void do_something_type_specific<int>() {...}
    template <typename Iterator>
    void perform_work_on_a_range(Iterator begin, Iterator end) {
        do_something_type_specific<typename Iterator::value_type>();
    }
    

    【讨论】:

    • 如果Iterator::value_typeshort 应该调用int 版本,因为short 可以转换为int
    猜你喜欢
    • 1970-01-01
    • 2011-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多