该代码是否执行多个实例化,以及如何避免它?

时间:2019-06-24 08:51:22

标签: c++ templates

假设我有一些类似如下的代码

Convert.hpp

#pragma once

#include <cassert>

class Converter
{
public:
  template <typename T>
  static inline void to_type(const char* bytes, T& val);
  static inline void to_int(const char* bytes, int& val);
};

#include "converter.inl"

Convert.inl

template <typename T>
void Converter::to_type(const char* bytes, T& value)
{
    auto numberOfBytes = sizeof(T);
    if (numberOfBytes == 8)
    {
        value = (T)(*(bytes + 3) << 40 | *(bytes + 3) << 32 | *(bytes + 3) << 24 | *(bytes + 2) << 16 | *(bytes + 1) << 8 | *bytes);
    }
    else if (numberOfBytes == 4)
    {
        value = (T)(*(bytes + 3) << 24 | *(bytes + 2) << 16 | *(bytes + 1) << 8 | *bytes);
    }
    else if (numberOfBytes == 2)
    {
        value = (T)(*(bytes + 1) << 8 | *bytes);
    }
    else {
        assert(false);
    }
}

void Converter::to_int(const char* bytes, int& value)
{
  to_type(bytes, value);
}

Userofconverter.hpp

#pragma once

bool isConverterUsed(const char* bytes);

Userofconverter.cpp

#include <iostream>

#include "converter.hpp"

bool isConverterUsed(const char* bytes)
{
  int myIntValue = 0;
  Converter::to_type(bytes, myIntValue);
  std::cout << "myIntValue: " << myIntValue << std::endl;
  return true;
}

converter.hpp的另一个用户

#pragma once

bool isConverterUsedAgain(const char* bytes);

converter.cpp的另一个用户

#include <iostream>

#include "converter.hpp"

bool isConverterUsedAgain(const char* bytes)
{
  int myIntValue = 0;
  Converter::to_type(bytes, myIntValue);
  std::cout << "myIntValue: " << myIntValue << std::endl;
  return true;
}

此代码除了帮助我说明问题外无所作为。当我使用g++ (Ubuntu 8.3.0-6ubuntu1~18.04) 8.3.0进行编译时(如果在WSL中很重要),我得到以下输出

$g++ main.cpp userofconverter.cpp anotheruserofconverter.cpp
In file included from converter.hpp:13,
                 from userofconverter.cpp:3:
converter.inl: In static member function ‘static void Converter::to_type(const char*, T&)’:
converter.inl:7:31: warning: left shift count >= width of type [-Wshift-count-overflow]
   value = (T)(*(bytes + 3) << 40 | *(bytes + 3) << 32 | *(bytes + 3) << 24 | *(bytes + 2) << 16 | *(bytes + 1) << 8 | *bytes);
                               ^~
converter.inl:7:52: warning: left shift count >= width of type [-Wshift-count-overflow]
   value = (T)(*(bytes + 3) << 40 | *(bytes + 3) << 32 | *(bytes + 3) << 24 | *(bytes + 2) << 16 | *(bytes + 1) << 8 | *bytes);
                                                    ^~
converter.inl: In instantiation of ‘static void Converter::to_type(const char*, T&) [with T = int]’:
converter.inl:24:23:   required from here
converter.inl:7:28: warning: left shift count >= width of type [-Wshift-count-overflow]
   value = (T)(*(bytes + 3) << 40 | *(bytes + 3) << 32 | *(bytes + 3) << 24 | *(bytes + 2) << 16 | *(bytes + 1) << 8 | *bytes);
               ~~~~~~~~~~~~~^~~~~
converter.inl:7:49: warning: left shift count >= width of type [-Wshift-count-overflow]
   value = (T)(*(bytes + 3) << 40 | *(bytes + 3) << 32 | *(bytes + 3) << 24 | *(bytes + 2) << 16 | *(bytes + 1) << 8 | *bytes);
                                    ~~~~~~~~~~~~~^~~~~
In file included from converter.hpp:13,
                 from anotheruserofconverter.cpp:3:
converter.inl: In static member function ‘static void Converter::to_type(const char*, T&)’:
converter.inl:7:31: warning: left shift count >= width of type [-Wshift-count-overflow]
   value = (T)(*(bytes + 3) << 40 | *(bytes + 3) << 32 | *(bytes + 3) << 24 | *(bytes + 2) << 16 | *(bytes + 1) << 8 | *bytes);
                               ^~
converter.inl:7:52: warning: left shift count >= width of type [-Wshift-count-overflow]
   value = (T)(*(bytes + 3) << 40 | *(bytes + 3) << 32 | *(bytes + 3) << 24 | *(bytes + 2) << 16 | *(bytes + 1) << 8 | *bytes);
                                                    ^~
converter.inl: In instantiation of ‘static void Converter::to_type(const char*, T&) [with T = int]’:
converter.inl:24:23:   required from here
converter.inl:7:28: warning: left shift count >= width of type [-Wshift-count-overflow]
   value = (T)(*(bytes + 3) << 40 | *(bytes + 3) << 32 | *(bytes + 3) << 24 | *(bytes + 2) << 16 | *(bytes + 1) << 8 | *bytes);
               ~~~~~~~~~~~~~^~~~~
converter.inl:7:49: warning: left shift count >= width of type [-Wshift-count-overflow]
   value = (T)(*(bytes + 3) << 40 | *(bytes + 3) << 32 | *(bytes + 3) << 24 | *(bytes + 2) << 16 | *(bytes + 1) << 8 | *bytes);
                                    ~~~~~~~~~~~~~^~~~~

在编译输出中,我可以看到两次此消息In instantiation of ‘static void Converter::to_type(const char*, T&) [with T = int]’

  1. 这是否意味着Converter :: to_type函数实际上被实例化了两次?
  2. 如果是这样,有没有办法避免多个实例化?

3 个答案:

答案 0 :(得分:2)

模板将被编译两次。这是因为内联函数是在标头中定义的,并且未编译标头,因此每个cpp文件必须编译其使用的每个内联函数的自身版本,以防万一其他cpp文件不使用内联函数。但是,如果编译器内联了内联函数,则仍然有必要,并且如果内联函数未内联,则链接器将删除重复项。

您可以在cpp文件中为Converter :: to_type 创建一个显式实例化,以防止其被编译两次。

// file Converter.h
#pragma once

#include <cassert>

class Converter
{
public:
  template <typename T>
  static inline void to_type(const char* bytes, T& val);
  ...
};
extern template void Converter::to_type<int>(const char* bytes, int& val);

...
// file Converter.cpp
#include "Converter.h"

template void Converter::to_type<int>(const char* bytes, int& val);

答案 1 :(得分:0)

Converter::to_type作为实例化跟踪的一部分,在错误消息中多次出现。为什么在特定的to_type实例中有两个错误?可能是因为您没有使用constexpr numberOfBytesif constexpr,所以to_type中的所有代码都必须有效并进行编译。如果if constexpr不可用,您仍然可以使用模板专门化来模拟它。

答案 2 :(得分:0)

您编译了3个cpp文件g++ main.cpp userofconverter.cpp anotheruserofconverter.cpp,命令行中的每个cpp文件都会生成一个Translation unit

每个翻译单元都会#include "converter.hpp",因为您告诉过它,并且在编译这两个翻译单元时,它们都在converter.hpp中发现了警告和错误,因此您会看到相同的消息。

是的,它可以解决,例如,您可以编译一个文件(例如main.cpp)并包含userofconverter.cppanotheruserofconverter.cpp,它将作为一个翻译单元编译。