I'm writing a templated container class and want constructors to differentiate between a single value to initialize all elements in the container (which is OK) and an array of values (whose size must match the size specified in the template instantiation).
Everything works great unless I specify a brace-initialized parameter of just one element for a template instance with SIZE > 1; unfortunately doing so picks the first constructor, and if the template is instantiated with SIZE > 1 but yet the user provided a brace-initialized parameter (array) of only one element, I want to catch that error with a static_assert
.
Here's a stripped-down version of my code that shows the problem:
#include <iostream>
template <typename TYPE, size_t SIZE>
class Array
{
public:
// CTOR #1: Initialize EACH ELEMENT from a single TYPE initializer
explicit Array(TYPE const value)
{
std::cout << "CTOR #1, SIZE=" << SIZE << "\n";
for (size_t i = 0; i < SIZE; ++i)
m_buf[i] = value;
}
// CTOR #2: Initialize from a C-style array of the same size
template <size_t N>
explicit Array(TYPE const (&values)[N])
{
static_assert(N == SIZE, "CTOR #2: Attempt to initialize from a C-style array of the wrong size.");
std::cout << "CTOR #2, SIZE=" << SIZE << ", N=" << N << "\n";
for (size_t i = 0; i < SIZE; ++i)
m_buf[i] = values[i];
}
template <typename T, size_t S>
friend std::ostream& operator<<(std::ostream&, Array<T, S> const&);
private:
TYPE m_buf[SIZE];
};
template <typename TYPE, size_t SIZE>
std::ostream& operator<<(std::ostream& os, Array<TYPE, SIZE> const& arr)
{
for (size_t i = 0; i < SIZE; ++i)
os << ' ' << arr.m_buf[i];
return os;
}
int main()
{
int CStyleArray1[1] = { 1 };
int CStyleArray6[6] = { 1, 2, 3, 4, 5, 6 };
int CStyleArray7[7] = { 1, 2, 3, 4, 5, 6, 7 };
int CStyleArray8[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
(void)CStyleArray6; (void)CStyleArray8; // avoid unused variable warnings
Array<int, 7> a(5); // ctor #1
//Array<int, 7> b(CStyleArray6); // ctor #2 fails due to static_assert(N==SIZE) (this is good!)
Array<int, 7> c(CStyleArray7); // ctor #2
//Array<int, 7> d(CStyleArray8); // ctor #2 fails due to static_assert(N==SIZE) (this is good!)
//Array<int, 7> e({ 1, 2, 3, 4, 5, 6}); // ctor #2 fails due to static_assert(N==SIZE) (this is good!)
Array<int, 7> f({ 1, 2, 3, 4, 5, 6, 7}); // ctor #2
//Array<int, 7> g({ 1, 2, 3, 4, 5, 6, 7, 8}); // ctor #2 fails due to static_assert(N==SIZE) (this is good!)
Array<int, 1> h(CStyleArray1); // ctor #2
Array<int, 1> i({1}); // ctor #1 (why did this not use ctor #2 with N=1?)
//Array<int, 7> j(CStyleArray1); // ctor #2 fails due to static_assert(N==SIZE) (this is good!)
Array<int, 7> k({1}); // ctor #1 (why did this not use ctor #2 with N=1, and fail the static_assert?)
std::cout << " a:" << a
//<< "\n b:" << b
<< "\n c:" << c
//<< "\n d:" << d
//<< "\n e:" << e
<< "\n f:" << f
//<< "\n g:" << g
<< "\n h:" << h
<< "\n i:" << i
//<< "\n j:" << j
<< "\n k:" << k
<< std::endl;
}
When I compile & run that, this is the output:
C:\GitHOME\test>g++ -O3 -std=gnu++11 -g -Wall -Werror test.cpp
C:\GitHOME\test>a.exe
CTOR #1, SIZE=7
CTOR #2, SIZE=7, N=7
CTOR #2, SIZE=7, N=7
CTOR #2, SIZE=1, N=1
CTOR #1, SIZE=1
CTOR #1, SIZE=7
a: 5 5 5 5 5 5 5
c: 1 2 3 4 5 6 7
f: 1 2 3 4 5 6 7
h: 1
i: 1
k: 1 1 1 1 1 1 1 <------ I expected this to use ctor #2 and fail the static_assert!
C:\GitHOME\test>
Is there any way I can get code like Array<int, 7> k({1});
to call ctor #2 so it triggers the static_assert? I'd like a brace-initialized list of one element to be considered an array of size [1], not considered a single value.