C++ 模板类的非模板方法的早期实例化

C++ 模板类的非模板方法的早期实例化,c++,c++17,language-lawyer,c++20,C++,C++17,Language Lawyer,C++20,考虑以下类别: #include <vector> template<class T> struct my_struct { my_struct() {} explicit my_struct(T) {} explicit my_struct(std::vector<T>) {} }; int main() { my_struct<const int> s1(1); } 正如我解释的那样,问题在于一旦实

考虑以下类别:

#include <vector>

template<class T>
struct my_struct {
    my_struct() {}
    explicit my_struct(T) {}
    explicit my_struct(std::vector<T>) {}
};

int main() {    
    my_struct<const int> s1(1);
}
正如我解释的那样,问题在于一旦实例化了
my_struct
,就会产生一个具体的类型,编译器可能会实例化它的所有方法。在本例中,它实例化了
myu结构(myu类)
,因此它实例化了
myu类
,因此我们得到了静态错误

注意:我们可能会得到“幸运”但是:在
vector
情况下,只调用
my_struct()
不会触发错误(我想这与重载解析有关)

注意:我修复错误的方法是通过模板化构造函数,而不是使用
vector

template<class vector_type>
my_struct(vector_type) {}
模板
my_结构(向量_类型){}
我觉得很难看,但没有其他想法

  • 我对正在发生的事情的解释正确吗
  • 语言是否要求/未定义此行为?还是一个编译器错误
  • 如果是的话,我至少会称之为C++17中的语言缺陷。你同意吗
  • 如果是,是否有语言缺陷报告?它在C++20中被更正了吗
  • 如果否:您将如何解决该问题?这是设计问题吗
  • 我们能指望编译器在不久的将来对正在发生的事情有一个更详细的堆栈跟踪吗?至少提到了为什么它需要实例化一个手头的具体类从未调用过的类型

  • T
    const
    时,可以使用
    std::enable_if
    禁用向量构造函数:

    #include <type_traits>
    #include <vector>
    
    template<class T>
    struct my_struct {
        my_struct() {}
        explicit my_struct(T) {}
        template <typename U = T>
        explicit my_struct(std::vector<U>, std::enable_if_t<!std::is_const_v<U>, int> = 0) {}
    };
    
    int main() {
        my_struct<const int> s(1);
        my_struct<int> t({1, 2});
    }
    
    #包括
    #包括
    模板
    结构我的结构{
    my_struct(){}
    显式my_结构(T){}
    模板
    显式my_结构(std::vector,std::enable_if_t,int>=0){
    };
    int main(){
    我的结构(1);
    我的结构t({1,2});
    }
    
    从C++20开始,您可以使用尾部requires子句引入的约束表达式,将
    my_struct
    类模板的给定实例化的第三个构造函数的实例化约束到类模板的类型模板参数
    T
    上的某个谓词:

    #include <type_traits>
    #include <vector>
    
    template<class T>
    struct my_struct {
        my_struct() {}
        explicit my_struct(T) {}
        explicit my_struct(std::vector<T>) requires (!std::is_const_v<T>) {}
    };
    
    int main() {
        my_struct<const int> s(1);
        my_struct<int> t({1, 2});
    }
    
    #包括
    #包括
    模板
    结构我的结构{
    my_struct(){}
    显式my_结构(T){}
    显式my_结构(std::vector)需要(!std::is_const_v){}
    };
    int main(){
    我的结构(1);
    我的结构t({1,2});
    }
    
    除了使用比C++20之前版本更简单的语法之外,std::enable_if_tSFINAE方法,它也不会强制您仅将构造函数设置为模板函数,这样当构造函数是重载解析中的候选函数时,构造函数本身就可以通过参与推断过程的类型模板参数进行参数化

    特别是,国家[强调我国]:

    [temp.constr.constr]/2

    为了实例化受约束的模板 ([temp.spec]),应满足其相关约束条件 在以下子条款中描述。[注:形成一个 类模板、变量模板或别名的专门化 模板([temp.names])需要满足其约束条件。 重载解决方案需要满足函数和函数模板的约束条件。结束注释]

    在这种情况下,特定的相关约束包括:

    [temp.constr.decl]/3

    声明的关联约束定义如下:

    • [……]
    • (3.3)否则,相关约束是逻辑AND表达式的标准形式,其操作数顺序如下:
      • [……]
      • (3.3.4)函数声明([dcl.fct])的尾随requires子句([dcl.decl])引入的约束表达式

    1)
    static_断言(0,“不应使用”)是,这与
    std::vector
    无关。2) 我建议您也添加语言标准标签。例如,C++17和C++20的解决方案可能会有所不同。我曾经得出结论,一个实现是有权利的,因为它的约束会影响程序的有效性。您似乎也遇到了类似的情况,尽管GCC在通过引用传递向量时并不担心。这就是问题所在。@Evg同时添加了C++17和C++20,因为我想要这两个的答案obviously@LanguageLawyer:必须实例化参数类型以执行重载解析,因为它可能可以从参数类型构造。(通过引用,允许实现跳过实例化([temp.inst]/9)。)您可能想说这里需要额外的模板参数。
    #include <type_traits>
    #include <vector>
    
    template<class T>
    struct my_struct {
        my_struct() {}
        explicit my_struct(T) {}
        explicit my_struct(std::vector<T>) requires (!std::is_const_v<T>) {}
    };
    
    int main() {
        my_struct<const int> s(1);
        my_struct<int> t({1, 2});
    }