草庐IT

c++ - clang 和 gcc 在处理模板生成和静态 constexpr 成员时的不同行为?

coder 2024-02-25 原文

考虑以下程序(抱歉太长了;这是我能想到的表达问题的最短方式):

#include <iostream>
#include <vector>
#include <typeindex>

using namespace std;

std::vector<std::type_index>&
test_vector()
{
  static std::vector<std::type_index> rv;
  return rv;
}

template <typename T>
class RegistrarWrapper;

template<typename T>
class Registrar
{
  Registrar()
  {
    auto& test_vect = test_vector();
    test_vect.push_back(std::type_index(typeid(T)));
  }
  friend class RegistrarWrapper<T>;
};

template <typename T>
class RegistrarWrapper
{
  public:
    static Registrar<T> registrar;
    typedef Registrar<T> registrar_t;
};

template <typename T>
Registrar<T> RegistrarWrapper<T>::registrar;


template <typename T>
class Foo
{
  public:
    // Refer to the static registrar somewhere to make the compiler
    // generate it ?!?!?!?
    static constexpr typename RegistrarWrapper<Foo<T>>::registrar_t& __reg_ptr =
      RegistrarWrapper<Foo<T>>::registrar;
};


int main(int argc, char** argv)
{
  Foo<int> a;
  Foo<bool> b;
  Foo<std::string> c;

  for(auto&& data : test_vector()) {
    std::cout << data.name() << std::endl;
  }

}

当使用 clang++(版本 3.5.2,当然使用 -std=c++11)编译时,此程序输出(通过 c+ +filt 以提高可读性):

Foo<int>
Foo<bool>
Foo<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >

但是对于 g++(尝试过版本 4.8.5、4.9.3 和 5.2.0),它什么都不输出!这里发生了什么?哪个编译器符合c++标准?我如何以与编译器无关的方式创建这种效果(最好没有任何运行时开销)?

最佳答案

首先,几个解决方案。对于它们两者,重要的部分是从保证实例化的代码中获取 registrar 的地址。这确保静态成员的定义也被实例化,从而触发副作用。

第一个依赖于 Foo 的每个特化的默认构造函数的定义被实例化以处理 ab 的默认初始化cmain 中:

template<typename T> class Foo
{
public:
   Foo() { (void)&RegistrarWrapper<Foo<T>>::registrar; }
};

缺点是这引入了一个非平凡的构造函数。避免此问题的替代方法如下:

template<class T> constexpr std::size_t register_class() 
{ 
   (void)&RegistrarWrapper<T>::registrar; 
   return 1; 
}

template<typename T> class Foo
{
   static char reg[register_class<Foo<T>>()];
};

这里的关键是在不依赖任何初始化器的情况下触发静态成员声明中的实例化(见下文)。

这两种解决方案在 Clang 3.7.0、GCC 5.2.0 和 Visual C++ 2015 中都运行良好,无论是否启用了优化。第二个使用 constexpr 函数的扩展规则,这是一个 C++14 特性。当然,如果需要,有几种简单的方法可以使其符合 C++11。


我认为您的解决方案的问题在于,如果在某处未使用其值,则不能保证 __reg_ptr 的初始化程序会被实例化。 N4527 的一些标准引述:

14.7.1p2:

[...] the initialization (and any associated side-effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist.

这并没有完全解决 constexpr 的问题,因为(我认为)它讨论的是 odr 使用的静态数据成员的类外定义(它更相关于registrar),但它很接近。

14.7.1p1:

[...] The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or exception-specifications of the class member functions, member classes, scoped member enumerations, static data members and member templates [...]

这保证了第二种解决方案有效。请注意,它不保证有关静态数据成员的类内初始化程序的任何内容。

constexpr 构造的实例化似乎存在一些不确定性。有 CWG 1581 ,这与我们的案例没有太大关系,只是在最后,它谈到了一个事实,即不清楚 constexpr 实例化是在常量表达式评估期间还是在解析期间发生。这方面的一些说明也可能为您的解决方案提供一些保证(无论哪种方式......)但我们必须等待。


第三种变体:使您的解决方案有效的一种方法是显式实例化 Foo 的特化,而不是依赖隐式实例化:

template class Foo<int>;
template class Foo<bool>;
template class Foo<std::string>;

int main()
{
   for(auto&& data : test_vector()) {
      std::cout << data.name() << std::endl;
   }
}

这也适用于所有三个编译器,并且依赖于 14.7.2p8:

An explicit instantiation that names a class template specialization is also an explicit instantiation of the same kind (declaration or definition) of each of its members [...]

鉴于这些是显式实例化定义,这似乎足以说服 GCC 为 __reg_ptr 实例化初始化器。但是,那些显式实例化定义在整个程序中只能出现一次([14.7p5.1]),因此需要格外小心。我认为前两种解决方案更可靠。

关于c++ - clang 和 gcc 在处理模板生成和静态 constexpr 成员时的不同行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33504651/

有关c++ - clang 和 gcc 在处理模板生成和静态 constexpr 成员时的不同行为?的更多相关文章

  1. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  4. ruby-on-rails - 无法在centos上安装therubyracer(V8和GCC出错) - 2

    我正在尝试在我的centos服务器上安装therubyracer,但遇到了麻烦。$geminstalltherubyracerBuildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingtherubyracer:ERROR:Failedtobuildgemnativeextension./usr/local/rvm/rubies/ruby-1.9.3-p125/bin/rubyextconf.rbcheckingformain()in-lpthread...yescheckingforv8.h...no***e

  5. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

    对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

  6. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  7. ruby-on-rails - Mandrill API 模板 - 2

    我正在使用Mandrill的RubyAPIGem并使用以下简单的测试模板:testastic按照Heroku指南中的示例,我有以下Ruby代码:require'mandrill'm=Mandrill::API.newrendered=m.templates.render'test-template',[{:header=>'someheadertext',:main_section=>'Themaincontentblock',:footer=>'asdf'}]mail(:to=>"JaysonLane",:subject=>"TestEmail")do|format|format.h

  8. ruby-on-rails - 如何从过时的 TZInfo 标识符中获取 Rails TimeZone 名称? - 2

    已经有一个问题回答了如何将“America/Los_Angeles”转换为“PacificTime(US&Canada)”。但是我想将“美国/太平洋”和其他过时的时区转换为RailsTimeZone。我无法在图书馆中找到任何可以帮助我完成此任务的东西。 最佳答案 来自RailsActiveSupport::TimeZonedocs:TheversionofTZInfobundledwithActiveSupportonlyincludesthedefinitionsnecessarytosupportthezonesdefinedb

  9. java - 为什么 ruby​​ modulo 与 java/other lang 不同? - 2

    我基本上来自Java背景并且努力理解Ruby中的模运算。(5%3)(-5%3)(5%-3)(-5%-3)Java中的上述操作产生,2个-22个-2但在Ruby中,相同的表达式会产生21个-1-2.Ruby在逻辑上有多擅长这个?模块操作在Ruby中是如何实现的?如果将同一个操作定义为一个web服务,两个服务如何匹配逻辑。 最佳答案 在Java中,模运算的结果与被除数的符号相同。在Ruby中,它与除数的符号相同。remainder()在Ruby中与被除数的符号相同。您可能还想引用modulooperation.

  10. ruby - Chef Ruby 遍历 .erb 模板文件中的属性 - 2

    所以这可能有点令人困惑,但请耐心等待。简而言之,我想遍历具有特定键值的所有属性,然后如果值不为空,则将它们插入到模板中。这是我的代码:属性:#===DefaultfileConfigurations#default['elasticsearch']['default']['ES_USER']=''default['elasticsearch']['default']['ES_GROUP']=''default['elasticsearch']['default']['ES_HEAP_SIZE']=''default['elasticsearch']['default']['MAX_OP

随机推荐