Class in a DLL to extract data from a file via many accessors
I think you can use templates to good use to simplify the user interface and still retain the flexibility.

I would suggest declaring some structs instead of using enum class DataTypes.

struct DataType_1;
struct DataType_2;
struct DataType_3;
struct DataType_4;


You may define them to be empty structs but that is not necessary for what I am going to suggest.

// This is OK too but not necessary.
struct DataType_1 {};
struct DataType_2 {};
struct DataType_3 {};
struct DataType_4 {};


Then create template class that captures the notion of traits corresponding to the above structs.

template &lt;typename T&gt; struct DataTypeTrait;

template &lt;&gt; struct DataTypeTrait&lt;DataType_1&gt;
{
   using type = int;
};

template &lt;&gt; struct DataTypeTrait&lt;DataType_2&gt;
{
   using type = int;
};

template &lt;&gt; struct DataTypeTrait&lt;DataType_3&gt;
{
   using type = float;
};

template &lt;&gt; struct DataTypeTrait&lt;DataType_4&gt;
{
   using type = char const*;
};


Create a wrapper class around the traits class.

template &lt;typename T&gt; struct DataType
{
   using type = DataTypeTrait&lt;T&gt;::type;
};


I understand that you have some concerns about using containers from the standard library. However, it's OK to use of member function templates described below since they don't involve use of standard library containers in the interface of the member functions.

Here's how I see FileLoader to look like:

class FileLoader
{
   public:
      FileLoader(const char* path);

      int GetIterationCount();

      // This is all you need in the public interface.
      template &lt;typename T&gt;
         typename DataType&lt;T&gt;::type getData(int iteration) const
         {
            return getData(iteration, (T*)nullptr);
         }

   private:

      // The dummy arguments allows the function overloading to
      // work seamlessly from the member function template.

      int getData(int iteration, DataType_1* dummy) const;
      int getData(int iteration, DataType_2* dummy) const;
      float getData(int iteration, DataType_3* dummy) const;
      char const* getData(int iteration, DataType_4* dummy) const;
};


User code

char const* filepath = "some/file/path";
FileLoader loader(filepath);
int val1 = loader.getData&lt;DataType_1&gt;(0);
float val2 = loader.getData&lt;DataType_3&gt;(0);
float val3 = loader.getData&lt;DataType_4&gt;(0); // Error.
char const* val4 = loader.getData&lt;DataType_4&gt;(0);