【问题标题】:Automatic template instantiation自动模板实例化
【发布时间】:2014-05-08 05:01:47
【问题描述】:

给定类模板

template<class T>
struct A {
    A( const T & x );
};

我想实例化此类的对象而不写出实际类型T,因为这通常是由某种表达式模板和 lambda 函数产生的笨拙类型。实现此目的的一种方法是

template<class T>
A<T> create_A( const T& t ){ 
    return A<T>( t );
}

int main(){
    auto a = create_A( complicated_expression );
}

这里我从未写过表达式的实际类型,但这会创建A 的副本,并且如果没有复制构造函数将无法工作。我没有复制(或移动)构造函数。我正在寻找的是类似

A a( complicated_expression );

简洁明了,编译器应该能够找出实际的类型。不幸的是,这不是有效的 C++。那么完成同样事情的最佳有效 C++ 语法是什么?目前我正在这样做:

auto x = complicated_expression;
A<decltype(x)> a(x);

但这似乎是不必要的冗长。有没有更好的方法来做到这一点?

【问题讨论】:

  • 从 create_A 返回 A* 不是一个选项吗?
  • “我没有复制(或移动)构造函数”你能提供吗?如果您关心性能,则不必这样做,因为编译器很可能无论如何都会忽略它,只需要定义它以便可以访问它。这似乎是做你想做的最惯用的方式。
  • @dlf 有趣的想法,谢谢。那时我必须考虑进行内存管理的最佳方法。
  • @Suma 实际上我并没有真正考虑过这一点,但现在正如你所提到的,是的,也许这就是要走的路。谢谢。
  • 你为什么假设函数create_A()会创建一个副本。我希望大多数编译器避免使用RVO 进行复制。至少在使用优化时。

标签: c++ templates


【解决方案1】:

代码

template<class T>
A<T> create_A( const T& t ){ 
    return A<T>( t );
}

int main(){
    auto a = create_A( complicated_expression );
}

正式要求复制或移动构造函数已定义且可用,但任何合理的编译器都可以使用复制省略规则对其进行优化。它是标准库中广泛使用的标准习惯用法(例如std::make_pair)。

【讨论】:

    【解决方案2】:

    好吧,如果你的编译器是 MSVC,这将起作用:

    template<class T>
    struct A {
        A(const T & x) {}
    
        A(const A&) = delete;
        A(A&&) = delete;
    };
    
    
    template <typename T>
    A<T> create_A(const T& t)
    {
        return t;
    }
    
    const auto& a = create_A(666);
    const auto& b = create_A(a);
    const auto& c = create_A(b);
    

    不过,clang 和 g++ 没有这样的运气。

    将值返回的结果分配给 const 引用是完全合法的,顺便说一下,has its uses。为什么 MSCV 避免检查类型是否可移动/可复制(即使它会优化它)对我来说是一个谜,并且可能是一个错误。但是,如果您需要这样做,它会在您的情况下起作用。

    编辑:或者,如果您不害怕将 C++ 众神的愤怒带到自己身上,您可以将 create_A 转换为宏:

    #define create_A(x) (A<decltype(x)>(x))
    

    现在它适用于所有编译器。

    EDIT2:正如@dyp 所建议的,这个答案可以进一步改进:

    template <typename T>
    A<T> create_A(const T& t)
    {
        return { t };
    }
    
    auto&& a = create_A(666);
    auto&& b = create_A(a);
    auto&& c = create_A(b);
    

    它适用于所有 C++11 编译器。

    【讨论】:

    • @JanHudec 让我注意到我认为 MSVC 会自动生成复制构造器。
    • 您可以使用auto&amp;&amp; a = create_A(666);template&lt;typename T&gt; A&lt;T&gt; create_A(const T&amp; t) { return {t}; } 这将不会调用A&lt;T&gt; 的任何复制/移动构造函数。
    • 哦,顺便说一句,如果你有 MSVC2013,你应该使用 = delete; 而不是将这些成员函数设为私有。我的版本应该适用于所有编译器,因为列表初始化避免了构造一个临时的。
    • @dyp 谢谢,太棒了。在这种情况下return t;return {t}; 之间有什么区别吗?
    • @pentadecagon 是的!例如。参见stackoverflow.com/q/7935639/420683 return {t} 直接从t 构造返回值,而return t 构造一个A&lt;T&gt; 类型的临时值并将其移动到返回值中(这需要移动或复制构造函数)。该复制/移动当然可以省略,但即使省略了也始终需要可访问的复制/移动构造函数。
    【解决方案3】:

    您尝试实施的技术称为template argument deduction

    所以,我认为您无法实现以下目标:

    A a( complicated_expression );
    

    因为这里A 是模板的名称,模板参数推导只发生在函数中。不在课堂上。使用上面的行,您将无法避免 missing template arguments 错误。

    【讨论】:

    • 好的,代码已删除,@JanHudec 你是对的。但是仍然可以实现 OP 希望不涉及函数就不可能。
    猜你喜欢
    • 2021-07-18
    • 1970-01-01
    • 1970-01-01
    • 2021-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-30
    • 1970-01-01
    相关资源
    最近更新 更多