Reputation: 285
Having two c++ programs. One is simple and the other is a little complex. But all have the same problem. The simple one has the following files. foo.h:
class foo{
static const int array[3];
};
const int foo::array[3] = { 1, 2, 3 }; <-------- Here is the line causing the error.
foo.cc:
#include "foo.h"
main.cc:
#include "foo.h"
int main()
{
}
while compile and link with the following command:
clang++ -c *.cc -std=c++17
clang++ *.o -o a.out -std=c++17
it reports the following error:
main.o:(.rodata+0x0): multiple definition of `foo::array'
foo.o:(.rodata+0x0): first defined here
clang-14: error: linker command failed with exit code 1 (use -v to see invocation)
For the complex one, having the following files. t_a.h:
#pragma once
#include "t_b.h"
class TA : public TB,
public TIT<TB, TA> {
public:
static const char* name() { return "TA"; }
};
t_b.h:
#pragma once
#include "t_r.h"
class TB {
TI<TB> t_i() const { return t_i_; }
private:
template <typename T, typename U>
friend class TIT;
TI<TB> t_i_{TI<TB>::kUkT};
};
t_i.h:
#pragma once
#include <string>
#include <stdint.h>
template <typename BaseT>
class TR;
template <typename BaseT>
class TI {
public:
const std::string& name() const;
int8_t id() const { return id_; }
bool operator==(TI other) const { return id_ == other.id(); }
bool operator!=(TI other) const { return id_ != other.id(); }
static const TI kUkT;
private:
friend class TR<BaseT>;
explicit TI(int8_t id) : id_(id) {}
int8_t id_;
};
template <typename BaseT, typename DerivedT>
class TIT {
public:
static const TI<BaseT> kT;
TIT() {
static_cast<BaseT*>(static_cast<DerivedT*>(this))->t_i_ = kT;
}
static bool classof(const BaseT* obj) { return obj->t_i() == kT; }
};
template <typename BaseT>
TI<BaseT> RST(const std::string& t);
template <typename BaseT, typename DerivedT>
const TI<BaseT> TIT<BaseT, DerivedT>::kT =
RST<BaseT>(DerivedT::name()); <-------- This block of code should cause a similar error, but it does not.
t_r.h:
#pragma once
#include <cassert>
#include <map>
#include <mutex>
#include <string>
#include <vector>
#include "t_i.h"
template <typename BaseT>
class TR {
public:
TR(const TR&) = delete;
TR& operator=(const TR&) = delete;
static TR& GI();
TI<BaseT> RT(const std::string& t);
const std::string& GTN(TI<BaseT> i) const;
private:
TR() = default;
mutable std::mutex mutex_;
std::vector<std::string> names_;
std::map<std::string, int8_t> name_to_id_;
};
template <typename BaseT>
TR<BaseT>& TR<BaseT>::GI() {
static TR<BaseT> r;
return r;
}
template <typename BaseT>
TI<BaseT> TR<BaseT>::RT(const std::string& t) {
std::lock_guard<std::mutex> guard(mutex_);
assert(name_to_id_.find(t) == name_to_id_.end());
assert(names_.size() < static_cast<decltype(names_.size())>(
std::numeric_limits<int8_t>::max()));
int8_t id = static_cast<int8_t>(names_.size());
names_.emplace_back(t);
name_to_id_[t] = id;
return TI<BaseT>(id);
}
template <typename BaseT>
const std::string& TR<BaseT>::GTN(
TI<BaseT> info) const {
std::lock_guard<std::mutex> guard(mutex_);
int8_t id = info.id();
assert(id >= 0);
assert(static_cast<size_t>(id) < names_.size());
return names_[id];
}
template <typename BaseT>
TI<BaseT> RST(const std::string& type) {
return TR<BaseT>::GI().RT(type);
}
template <typename BaseT>
const std::string& TI<BaseT>::name() const {
return TR<BaseT>::GI().GTN(*this);
}
template <typename BaseT>
const TI<BaseT> TI<BaseT>::kUkT =
RST<BaseT>("Uk");
use_t_i_1.cc:
#include "t_a.h"
TIT<TB, TA> test_class_1;
use_t_i_2.cc:
#include "t_a.h"
int main() {
TIT<TB, TA> test_class_2;
}
When compile and link by the following command:
clang++ -c *.cc -std=c++17
clang++ *.o -o a.out -std=c++17
No error occurs. What could be the cause why the same class of grammar mistake exists in both of the two programs while one reports error but the other one does not? Could anyone please explain this? Could anyone please does a small adjustment to the second complex program so it also errs with the same class of error? Thanks in advance.
Upvotes: 0
Views: 96
Reputation: 3432
C++ explicitly allows multiple definitions of templated entities:
There can be more than one definition of a
- class type ([class]),
- enumeration type ([dcl.enum]),
- inline function or variable ([dcl.inline]),
- templated entity ([temp.pre]),
- default argument for a parameter (for a function in a given scope) ([dcl.fct.default]), or
- default template argument ([temp.param])
So it does not caused the multiple definition error.
Upvotes: 1
Reputation: 50100
this line
const int foo::array[3] = { 1, 2, 3 };
needs to be in one .cc file, not in a header. Just pick one
Upvotes: 1